Repository navigation
Releases: ascendaone-com/ai-engineer-tools
Releases · ascendaone-com/ai-engineer-tools
Release list
v0.1.39
setup always shows its link
- The link and the study notice print before setup starts waiting. They
used to print only after the first check came back unconfirmed, so a pairing
confirmed quickly could finish without ever showing them. Bothparticipate
and each agent's ownsetupare fixed.
One confirm for the first agent in participate
- Pairing and joining happen in one visit.
participate <code>sends
your code with the first agent's pairing. Open the link, check the
organisation and what you'd share, confirm, and the agent is paired and the
study joined. Declining refuses both. - Other agents pair on their own. They don't carry the code, since the
join is yours and not theirs. Two unpaired agents take two confirms, down
from three. - No typed code for that pairing. The app turns a typed code away for a
pairing that carries a join, so only the link is printed. - An older server still works. If the pairing response has no
studyJoin,
participatepairs the agents as usual, then joins with the separate
question and one more confirm. A re-run on a joined machine asks for
nothing. - A refused code still pairs. A code that isn't found, has nothing live,
or names the wrong mode is reported in the summary. The agent pairs without
the study. - The local dev server takes the code on a pairing too, so you can try the
flow without a backend.
Which operating system
- Events now include the operating system the collector runs on (
darwin,
win32,linux,wslorother), and nothing more specific. WSL counts
as its own value, since it runs on a Windows machine.
What's Changed
- feat(collector): every event says which operating system sent it by @ascendaone-com in #192
- participate: pair and join the study in one confirm by @ascendaone-com in #191
- chore: release notes for v0.1.39 by @ascendaone-com in #195
Full Changelog: v0.1.38...v0.1.39
v0.1.38
Confirm in a browser
setupandpairprint a link. Open it, sign in, and the pairing is
confirmed. You don't need the Mac app. The 6-digit code still works in the
app, and it's printed on the next line.join <code>prints a link too. It opens a page that shows the study,
then asks you to confirm or decline. The app takes the same code.- Study mode itself still needs the app.
joinoffers Report mode only, as
before. - Against a server without these pages, like the local dev server, both
commands name the app, as they did.
History import without the Flow app
ascenda-history-import import --shipfinds your pairing. It reads
~/.ascenda/credentials.jsonand the token the hooks'setuporpair
wrote, so a machine with no Flow app and no exported variables can ship.
Variables still win over the files, and the new--installation-id,
--token-fileand--api-base-urlflags win over both.- Tokens get renewed before they expire. With under 21 days left the
importer rotates the token and saves the new one where the hooks read it. An
expired token stops the run in the first second with thepaircommand to
fix it. Nothing has been read or copied by then. schedule installsets up a recurring import. It writes a launchd agent
on macOS and a systemd user timer on Linux, every 14 days unless you say
--every. On Windows it prints theschtasksline.schedule statusand
schedule removedo what they say.- Claude Code deletes transcripts after 30 days unless
cleanupPeriodDayssays
otherwise, soschedule installwon't accept an interval the retention
can't cover twice.fix-retention --applylifts the limit. So does
--override-retention. - A refusal for consent now says where to fix it. The message names the
web as well as the Flow app. A store left out of your grant is skipped and
named while the other stores keep shipping. - Without the Flow app the import prints "no Flow app on this machine, none
needed" where it used to say a container was missing.
One command to take part in a study
participate <code>sets a machine up for your organisation's study.
Runnpx -y @ascenda-one/history-import participate <code>with the code
your organisation gave you. It finds Claude Code, Codex, Cursor, Windsurf
and Gemini CLI, installs each one's hooks for every project and pairs it,
joins the study in Report mode, and sends the history already on the
machine. No desktop app needed.- It tells you up front how many browser confirms it needs: one per agent
that isn't paired yet, then one for the study. Each is announced before its
code appears. - Claude Code deletes transcripts after 30 days.
participateshows the
setting it would change and asks first.--fix-retentionand
--keep-retentionanswer for you. - The history is sent only when the study granted importing it. Otherwise
the step is skipped, and the summary says why. - Run it again to finish a half-done setup. Paired agents stay paired, a
study you joined isn't joined twice, and history already sent comes back as
already there. - The summary at the end lists which agents are collecting, the study and its
window, Claude Code's retention before and after, and how many events were
accepted or already there.
History import
import --summary-json <file>writes the closing summary as JSON too.- A missing pairing during
import --shipis reported in the summary, after
the stores are read. It used to end the run before the summary printed. An
expired token still stops the run before anything is read.
Local dev server
- Historical events need the history-import consent, which a confirmed join
that carries it grants. Start the server withhistoricalImportLease: false
to begin without it. It's on by default, soimport --ship --localworks
as before. - The batch door answers
duplicatefor animportKeyit has already
stored, and returns a result for every event in the batch.
History import: compactions and commits
- Each compaction gets its own row.
ascenda-history-importnow writes a
context_compression_manualorcontext_compression_autorow for every
compact_boundaryin a Claude Code transcript, stamped when it happened.
Before, a session wrote one row per kind at its last timestamp. Each new
row carriescompactionOrdinal, its place in the session. - If you imported before, run it again. The new rows are added beside the
old session-end ones; nothing is removed. A reader can tell the old rows by
their missingcompactionOrdinal. import --gitreads commit history. Off unless you pass it. It looks
only at repositories your agent sessions ran in, and sends your own commits
as time, size buckets, files changed and merge or not. No messages, paths,
names or emails. The backend has to accept thegitstore first, so for
now the rows are written to the events file, and a--shiprun may have
them refused.- Codex rollouts mark a compaction but not whether it was manual or auto, so
Codex gets no per-compaction rows.
What's Changed
- feat: setup, pair and join print a link to confirm in a browser by @ascendaone-com in #187
- feat(history-import): ship without the Flow app, and on a schedule by @ascendaone-com in #188
- feat(history-import): participate
, a study from one command by @ascendaone-com in #189 - History import: a row per compaction, and an opt-in git commit extractor by @ascendaone-com in #190
Full Changelog: v0.1.37...v0.1.38
v0.1.37
Claude Code plugin users: update to plugin 0.3.0 with
claude plugin update ascenda@ascenda-one, then restart Claude Code.
setup installs have nothing to re-run.
Plugin hooks start without npm
- Hooks run from a copy on your machine. Plugin 0.3.0 runs every hook and
the MCP server from~/.ascenda/plugin, so a hook starts in under a tenth
of a second. Each one used to ask npm for the latest release first. That
cost half a second online. On a network that hangs it outlasted the 5
seconds Claude Code allows a hook, and the event was lost. - Once a day the copy updates itself in the background. A new release reaches
you within a day. - Your first session after updating still goes through
npx, while the copy
downloads. ASCENDA_DISABLE_UPDATE_CHECK=truekeeps the copy you have.
Hooks registered by hand
- The example runs what
setupinstalls.
examples/settings.local.json
names the hook launcher in~/.ascenda/bin, so runsetupfirst. Hooks
copied from the old example callnpxand have the same problem as the old
plugin. Swap them for the new ones. setup,statusanduninstallrecognise these entries in the files they
manage: your user settings and a project'ssettings.local.json. They
couldn't see thenpxones. A shared.claude/settings.jsonisn't one of
those files, so keep an eye on that one yourself.
What's Changed
- chore(plugin): 0.2.2, for the permission hooks in v0.1.36 by @ascendaone-com in #180
- docs(claude-code-hooks): the manual hook example runs the launcher setup installs by @ascendaone-com in #185
- feat: a plugin launcher that runs a local bundle, and plugin-sync to keep it current by @ascendaone-com in #183
- chore: release notes for v0.1.37 by @ascendaone-com in #186
Full Changelog: v0.1.36...v0.1.37
v0.1.36
Re-run setup for each agent after upgrading. It adds the new Claude Code
permission hooks and Codex SessionEnd.
Hooks return before the network does
- A hook queues its events and gets out of the way. Every collector now
writes to its outbox, starts a background sender and returns, so your agent
waits for Node to start, not for a round trip. On a network that hangs, a
hook returns in a fraction of a second. Until now it held the agent for its
whole timeout. doctorsays which delivery mode is in use.ASCENDA_HOOK_DELIVERY=inline
sends before returning, as before, and a hosted Claude Code session always
does.
Setup that leaves other tools alone
- Your other hooks stay put. When another tool has added its own command
to a hook group Ascenda wrote,setupanduninstalltake out Ascenda's
entry and keep the rest. They used to remove the whole group. - Moved config folders work. With
CLAUDE_CONFIG_DIRorCODEX_HOMEset,
setup,statusanduninstalluse that folder, and the history importer
reads from it.CLAUDE_CONFIG_DIRcan list several folders, comma-separated. - Settings files are written atomically. A symlinked
settings.jsonstays a
symlink, and.ascenda-backupkeeps the file as it was before your first
install.
Codex
statusanddoctorname hooks Codex hasn't approved. Codex skips an
unapproved hook without a word, and a changed command needs approving again.
Each one is listed, with the fix.SessionEndis registered with a 3 second timeout, the most Codex allows
for it.- An older
setupwon't replace a newer hook runner.
Commits and pull requests from every agent
- Codex, Gemini CLI and Cursor report git work too. Commits, pushes,
reverts and pull requests opened now come from all three, as they already
did from Claude Code. Only a call that succeeded counts. Windsurf doesn't
say whether a command worked, so it reports none. git -C <dir> commit,git -c key=valueand--no-pagerare recognised.
So is a pull request opened through an MCP tool.- Codex's
exec_commandandshell_commandcount as shell calls, and the
functions.prefix Codex Desktop adds is understood. - Claude Code
NotebookEditcounts as an edit. Lines changed come from the
edit's own patch when Claude Code sends one, so the live count and the
imported count agree.
Prompts are things you typed
- A
!shell command in Claude Code isn't a prompt, live or imported. - Neither is a background task's report, which Claude Code hands back to the
session as if it were one. - Imported sessions driven by the Agent SDK keep those lines out of the
typed-prompt count.
History import counts each thing once
- Claude Code tokens count once per API response. Claude Code writes one
line per content block and repeats the response's usage on every one, so
token totals came out at about twice the real figure, or more. Resumed and
copied sessions don't count their parent's work again, tool calls included,
and a copied subagent's minutes stay with the session that ran it. - Codex threads fold properly. A fork keeps its own id and skips the
history it replays from its parent. A subagent thread joins the session that
started it. A long thread split over several files is one session.
Compressed.jsonl.zstrollouts are read. - Cursor and VS Code. A Cursor session starts at its first message, and
tokens Cursor didn't record are marked as not collected. VS Code chats with
no folder open are read, and so are Code - Insiders and VSCodium. - Claude Code cache writes and advisor-model tokens are counted. Older flat
agent-*.jsonlsubagent files join their parent session.
More in agent-event/v0
Every addition is optional. Nothing is renamed or removed.
- Match a session to its pull request.
repoKeyandbranchKeyare on
events from every agent, and the GitHub collector now sends
pull_request.closedandpull_request.mergedwith the pull request's
times.docs/TEAM_COLLECTION.mdshows the join. - The keys are unsalted. Anyone holding your events can check a guess at a
repository or branch name. They're set only for github.com, never on a
default branch, andsetup --no-repo-keysorASCENDA_REPO_KEYS=offturns
them off. - Permission prompts. Claude Code reports
permission.requested, and
permission.deniedin auto mode. No hook reports your answer, so there's no
event for it. - Also new:
vcsAction,promptId,modelonsession.started,
permissionMode, and areasononagent.waiting. examples/otlp-forwardersends the local event log to an OpenTelemetry
collector. The package README maps each field to its OpenTelemetry name.
What's Changed
- docs: keep measured counts in public, cut the content behind them by @ascendaone-com in #173
- test: pin the Codex session id across turns by @ascendaone-com in #170
- docs: say how the active-time split differs from Claude Code's own metric by @ascendaone-com in #171
- fix(hooks): report unapproved Codex hooks, register Codex SessionEnd, guard the runner against downgrades by @ascendaone-com in #166
- fix(claude-code-hooks): a task notification isn't a prompt by @ascendaone-com in #161
- fix(history-import): count Claude Code tokens once per message, and copied lines once by @ascendaone-com in #160
- feat(tool-kit): hooks queue their events and a background sender delivers them by @ascendaone-com in #162
- fix(history-import): a
!shell command isn't a prompt by @ascendaone-com in #163 - fix(hooks): keep other tools' hooks in shared groups, and follow CLAUDE_CONFIG_DIR and CODEX_HOME by @ascendaone-com in #164
- Report git and milestone facts from every hook adapter by @ascendaone-com in #167
- feat: repo and branch keys, vcsAction, a wait reason, promptId and model on the agent event by @ascendaone-com in #174
- fix(history-import): fold Codex threads properly, read .zst rollouts, honour CLAUDE_CONFIG_DIR and CODEX_HOME by @ascendaone-com in #168
- fix(history-import): count a copied subagent's minutes in the session that ran it by @ascendaone-com in #169
- fix(claude-code-hooks): a
!shell command isn't a prompt by @ascendaone-com in #165 - feat(history-import): read flat subagent files, SDK prompts, cache writes and more VS Code chats by @ascendaone-com in #172
- feat: permission events, a permission mode, an input wait reason, prompt ids and repo keys for every CLI agent by @ascendaone-com in #178
- feat(github-collector): key a pull request to its session and record how it ended by @ascendaone-com in #175
- docs: OpenTelemetry mapping, an OTLP forwarder, and joining sessions to PRs by @ascendaone-com in #176
- chore: release notes for v0.1.36 by @ascendaone-com in #179
Full Changelog: v0.1.35...v0.1.36
v0.1.35
Your agents' events, on your own machine
- An install without pairing keeps a log. Set up with
--no-pair, or
with a pairing that didn't finish, every agent now writes each event to
~/.ascenda/events.jsonl. It's the same metadata a paired install would
send, and nothing leaves the machine. Until now Codex, Cursor, Gemini CLI
and Windsurf kept nothing locally, and Claude Code did only if you'd
exportedASCENDA_EVENT_LOG_FILE. - You choose where it goes.
setup --event-logkeeps the file on a
paired install too,setup --event-log <path>moves it, and
setup --event-log offstops it. The setting covers every agent on the
machine, the VS Code extension included, and a re-pair keeps it.
ASCENDA_EVENT_LOG_FILEstill wins for one shell, and takesoffas well. setup,statusanddoctorshow the file. Each names the path and
why the log is on.- The file rotates at 5 MB, keeps one older copy, and only your user can
read it.
An open event format: @ascenda-one/agent-events
- Every line of the log carries an
event. It says which agent, what
happened, which tool and how it ended, in a small format called
agent-event/v0. It holds nothing the collectors didn't already record. - The format has its own package.
@ascenda-one/agent-eventsis new on
npm: a JSON Schema, TypeScript types and a reader for the log. No
dependencies, no account. - It's experimental. Fields and kinds can be added in any release.
Anything renamed or removed is listed in the notes for the release that
changes it. The package README has the full policy. examples/local-event-readerin the repo reads the file with plain Node.
It sums up each session, or follows the log live with--follow.
Licences
- Every package on npm now ships its licence and a NOTICE naming Ascenda One
Pty Ltd.agent-mcp,github-collectorandhistory-importwent out
without a LICENSE file before.
What's Changed
- chore: name Ascenda One Pty Ltd in the notices, and fix the pairing diagram by @ascendaone-com in #151
- An unpaired install keeps a local event log, and an example reads it by @ascendaone-com in #155
- Publish an experimental v0 agent event format, and write it on every log line by @ascendaone-com in #156
- refactor(tool-contract): split the event payload into envelope and body by @ascendaone-com in #154
- docs: add CONTRIBUTING.md by @ascendaone-com in #152
- chore: issue forms for compatibility reports and new integrations by @ascendaone-com in #153
- chore: release notes for v0.1.35 by @ascendaone-com in #158
- docs: open the README with what the collectors produce, and what's built on them by @ascendaone-com in #157
- docs: open the README for teams building their own analytics, and add a team collection guide by @ascendaone-com in #159
Full Changelog: v0.1.34...v0.1.35
v0.1.34
Claude Code hooks work on a fresh account
- Hooks load in every project.
setupregisters them in
~/.claude/settings.jsonby default. Run from your home folder, it used to
write a file Claude Code reads only when started in the home folder. Re-run
setupand it moves an existing install for you.--scope projectstill
works. - Hooks find Node on their own. Each hook now runs a small launcher that
finds Node 20 or newer, so they keep working when Claude Code opens from the
Dock or an IDE, and after nvm or fnm removes the version that ransetup. statusanddoctorsay whether anything is running. Both name the
Node your hooks will use and the last hook Claude Code ran.doctoralso
checks who is listening for the live signal and pings the Waterline screen
saver to prove a signal arrives.- The screen saver catches up when it starts. Signals sent while nothing
was listening are kept for half an hour, so an agent waiting on you before
the saver started still shows as waiting. Closing Claude Code mid-turn now
ends that session's water straight away. - An install without pairing says it's ready.
setup --no-pairtells you
the screen saver works now and that pairing is optional. In a terminal,
setup,statusanddoctormark each line and use colour.
Codex, Cursor, Gemini and Windsurf hooks work on a fresh account too
- The same install rules.
setupregisters hooks for every project by
default and moves an older project install. Each hook runs through the
launcher that finds Node. Windsurf run from your home folder used to write a
file it never reads. - Each adapter has
doctor. It names the Node, where the hooks are
registered and the last hook the agent ran, then checks the live signal. - Quitting Cursor or Gemini CLI mid-turn ends that session's water.
What's Changed
- Hooks work on a fresh account, for every agent by @ascendaone-com in #150
Full Changelog: v0.1.33...v0.1.34
v0.1.33
Claude Code's subagents are counted
- Each subagent sends a start and a stop. Claude Code's
SubagentStartand
SubagentStophooks are registered, and each sends Claude Code's id for the
subagent under the session that spawned it. That's how Ascenda can tell how
many agents you had running at once. A session that fans out to eight
subagents used to count as one. - Its name stays on your machine. A subagent you defined is sent as
custom, and one from a plugin asplugin. Built-in agents likeExplore
andPlanare sent by name. What a subagent was asked and what it said back
are never read. - Tool calls made inside a subagent carry its id too.
setupandpairlist subagents among what's sent. Re-runsetupto pick
up the two new hooks, or update the plugin.statusnow counts thirteen.
Your connection renews before it lapses
- Tokens renew while they still work. A paired install used to stop
sending 30 days after its last renewal, and onlypairbrought it back.
Now it renews once fewer than 21 days are left. Installs paired before this
release renew too, as long as their token is still live. - If yours has already lapsed, pair again. Claude Code's
pairdoes it. setuppairs again for Codex, Cursor, Gemini CLI and Windsurf. It
checks the saved token with the server before keeping it. If it's been
revoked or is past its 30 days, you get a pairing code, and the new pairing
keeps your installation id. Before this, setup kept the dead token and said
all was well.- Offline, setup keeps the token it has. If the last send was refused, it says
so and asks you to run setup again once you're back online.
Flow can tell an agent working from one waiting on you, or one that stopped
- The hooks now tell the Flow app on your Mac when an agent is waiting on
you. In Claude Code that's a permission prompt, anAskUserQuestionor
an MCP form. In Codex it's an approval. In Gemini CLI it's a tool
confirmation. Until now a parked approval looked like work for up to three
minutes. - Background work keeps a session open. When Claude Code stops a turn
with shells or subagents still running, the hook tells the app how many.
The waterline doesn't drain and the settle bell won't ring "All quiet"
while they run. After an hour with no word from that session, the app stops
trusting the count and treats it as gone quiet. - Claude Code's
StopFailurehook is registered. It fires when an API
error, like a usage limit or an expired login, ends a turn. The app hears
that the work has stopped, and whether it hit a usage limit, so the
waterline drains straight away. - A usage limit says when it lifts, if Claude Code will carry on by
itself. WithautoContinueAtUsageLimiton, the hook reads the reset
time from Claude Code's message and hands the app the time alone. Away
mode keeps your Mac awake until the reset, so the agent can pick up where
it stopped. Weekly limits don't count. Anything more than six hours off is
left out. - Pressing Esc gets the same treatment, once Claude Code shows its idle
prompt. Esc runs no hook, so until now the app waited three minutes
before deciding a session had gone quiet. - Gemini CLI registers one more hook,
Notification, for tool
confirmations. - Cursor and Windsurf have no hook for waits, so nothing changes there.
- Nothing new is sent to Ascenda. These signals stay on your machine, on the
same local socket the gauges already read, and carry no words: not the
question, not the command, not your answer.
Upgrading picks up the new hooks, and Flow says when you haven't
- Run
setupagain to upgrade. Use the scope you installed with. It keeps
your pairing, adds the hooks this version registers and prints what changed:
upgraded: added StopFailure, Notification; hook set 1 → 2. A run with
nothing to add saysalready current. - Flow names the command when your hooks are older. Each hook now tells
the app which set of hooks it was registered with, and whether it came from
the plugin. When Claude Code or Gemini CLI signals come from
an older set, Connections → Ingest telemetry shows one line with the
exact command. Dismiss it and it stays gone until a later release adds hooks
again. statusprints the hook set, and the upgrade command when it's behind.setupwarns when the other scope already registers the same hooks. Both
fire, so every event runs twice.- Plugin installs upgrade with
claude plugin update ascenda@ascenda-one. The
plugin is now 0.2.1, so the update reaches you.
Counted unless you object
statusshows what your organisation counts unless you object. An
organisation can record that it counts some of its own study on a basis it
has documented, like legitimate interests or a collective agreement, rather than
asking each of you to switch it on. For each of those,statusnames the
organisation's basis and its document, who recorded it, and that Ascenda
processes it for them. Then it tells you whether you've objected, and the
one command that changes it.object <purpose>stops you being counted for that purpose from the
next report. No reason asked.object <purpose> --undocounts you again.
The purposes aretelemetry,importandwork-patterns, and only those
three can ever be counted this way. Reports already issued stay as they are.- Yours alone. Nothing tells your organisation who objected, and
status
shows nothing about anyone else. Likejoin,objectneeds a real
terminal, so an agent can't run it for you. - With an organisation that counts nothing this way, or a server that
doesn't support it yet,statuslooks exactly as it did.
The agent can read what the Reveal showed you
- A third skill in the Claude Code plugin,
ascenda-reveal-read. Once
per session it reads, from the Flow app on your own Mac, the read the app
showed you when you imported your history: which days ran heavy, how the
weeks went, the hour band your work leans to, one crossing of two facts,
and the receipt. It holds that and says nothing unless you ask, or a piece
of work has to be placed in the day. - Counts and dates only. Your projects reach it as opaque digests, and
the bird you kept never does. - Needs the Flow app with
get_revealon its local server (Flow 0.1.16 or
later) and an agent paired with the Demand & workload scope. Without
either, the skill is silent.
See your organisation's changes in status
statuslists the changes your organisation has published to you. Each
one shows its title, the organisation's own summary, the direction it
expects, the scoring window, and the result once it's scored. A withdrawn
change says it's withdrawn and keeps its result. A status the tool doesn't
recognise reads "Status not recognised."- Nothing to list says why. "None published to you" means the list came
back empty. If the server turned the request down,statussays it couldn't
check and how to pair again. If the list couldn't be fetched, it gives the
reason and you can runstatusagain. An install that holds no token asks
for nothing and says that.statuscan't yet tell "not in any
organisation" from "nothing published". - Only
statusshows it. The hooks never fetch the list, and none of it is
passed to your agent.
Join an organisation's study, from the command line
join <code>, besidesetup,status,uninstallandpair— every
CLI agent adapter has it. Joining puts you in Report mode for 30 days; a
fuller Study mode still needs the Ascenda app, and the command says so.- One question, nothing preselected. You are shown the organisation, the
study, its window, and every grant Report mode carries, before you are
asked anything. "Not now" is the default. Enter, Ctrl-D, or running it from
a script all land there, and you are never joined. - No flag answers the question for you. The command also refuses to run
without a real terminal, because only you should be able to join yourself
to a study. - Saying yes prints a short code you confirm in the app, the same way
pair
does today, then shows what was granted. Each grant can be turned off on
its own, in the app's consent settings, and the organisation only ever
sees group counts, never who joined.
The GitHub collector records only the person who paired it
- Set
ASCENDA_FORGE_LOGINto your own GitHub login, typed once. The
example workflow and the README used${{ github.actor }}, which is
whoever triggered the run. A colleague's pull request or review then
matched and was recorded under your installation, and a review request
never matched you, because its actor is the person asking. - If you copied the old example, change that line. Store your login as a
repository secret, sayASCENDA_FORGE_LOGIN_OCTOCAT, and read it from
there. The collector can't tell a typed login from the actor, so the fix
is in your workflow file. - One step per person. The installation secrets belong to one person, so
a repository several people have paired in needs a step each, or a matrix
over their suffixes. The README shows both.
What's Changed
- feat: join an organisation's study from the command line by @ascendaone-com in #134
- chore: describe the summed hands-on figure from the collector's side by @ascendaone-com in #135
- feat: list your organisation's published changes in status by @ascendaone-com in #136
- feat(skills): ascenda-reveal-read, the agent reads what the Reveal showed by @ascendaone-com in #137
- Show what your organisa...
v0.1.28
Install the CLI hooks without an account
setup --no-pair. Every CLI agent'ssetupstopped at a pairing code
you confirm in the Ascenda app, so the hooks could not be installed at all
without an account. With the flag they install and register as usual, and
emit only the local live signal — the message a listener on this machine
reads to know an agent is working right now. Nothing is sent anywhere,
which was already what an unpaired hook did.- A pairing that cannot finish now installs the local half anyway, and
says so. Before, you got nothing at all. An unreachable host, an unconfirmed
code and an expired session all land in the same place. - Your installation id is recorded either way, so pairing later attaches
the hooks you already installed rather than minting a second id beside
them. Pair whenever you like by runningsetupagain. - On such an install the hooks stop reporting the absent pairing as a fault:
no journal entry per event, andstatussaysinstalled, not paired — local features active, telemetry inactive. A token that is revoked or
deleted after pairing is still reported, loudly, as before. - Claude Code is in this too. Its adapter takes the same flag, records the
same installation id and reads the same way instatusanddoctor, which
now name the mode:installed, not paired. - The session prompts keep working unpaired. The lines these hooks add to a
Claude Code session are written on your machine, so they never needed an
account either. Same for the live signal. statusstops calling a good install broken when you ask about the wrong
scope. Hooks in~/.claude/settings.jsonapply to every project, so a
check run in a project now finds them and says where they are.uninstallleaves your other agents alone. It used to delete the whole
credentials file, taking any other agent's pairing with it. It clears its own
entry now, and on an unpaired install it tells you there is no token to
revoke.
Every event names the collector that sent it
- Events carry
collectorVersion, the release of the collector that sent
them. A collector older than an event type sends nothing for it, and this is
how a zero from an old build can be told apart from a real one. A build from
a checkout saysunreleased. statusprints it first, for Claude Code and the other CLI agents alike.
Cloud sessions are told apart from local ones
- Claude Code events say where the agent ran:
runtimeislocalon your
machine andcloudin a hosted session such as Claude Code on the web. A
cloud session's transcript never reaches your machine, so a history import
can't see it and the hooks are its only record. The setup screen says so. - In a cloud session the hooks never rotate their token. Each session
starts from the token you put in that environment's settings, and a rotation
would revoke that copy for every later session. A rejected token there is
recorded as a failed send. Pair again and replace the token.
Claude Code sessions end
- The Claude Code hooks send a session end when Claude Code exits, the
samesession_endedevent Cursor, Gemini and the editor extensions send.
Until now a Claude Code session only ever started. - It carries Claude Code's own reason, one word:
clear,resume,logout,
prompt_input_exitorother. A word it doesn't recognise goes out as
unknown. The setup screen says so. - Exiting never waits on the network. The end goes into the outbox, and
the next hook to send delivers it: another open session's next tool call, or
your nextSessionStart. It keeps the time it was written. - Run
setupagain to pick it up. It registers ten hooks now, and
statuscounts10/10. The plugin registers the same ten.
What's Changed
- Give the plugin's hooks the same 5s timeout setup writes by @ascendaone-com in #125
- feat(cli-agents): setup --no-pairing installs the hooks without an account by @ascendaone-com in #122
- fix(tool-contract): name the reader that actually resolves two keys by @ascendaone-com in #127
- refactor(cli-agents): one unpaired install, matching the Claude Code adapter by @ascendaone-com in #128
- Say what a collector sends, before it pairs — and bind every key to what was said by @ascendaone-com in #124
- Claude Code hooks install without a pairing by @ascendaone-com in #126
- Every event names the collector version that sent it by @ascendaone-com in #129
- feat(claude-code-hooks): send a session end on SessionEnd by @ascendaone-com in #130
- chore: keep three comments to the collector's own reasoning by @ascendaone-com in #131
- chore: describe the backend from the collector's side in comments and docs by @ascendaone-com in #132
- feat(claude-code-hooks): tell cloud sessions apart, never rotate there by @ascendaone-com in #133
Full Changelog: v0.1.27...v0.1.28
v0.1.27
A leftover socket file can't swallow live signals
- The hooks skip a dead socket. Every hook sends a small live signal
to a local socket so a desktop listener knows an agent is working right
now. It went to the first socket file it found, and a file stays behind
when its listener is force-quit, crashes or is removed without cleaning
up. From then on every signal went into that dead file, a listener
further down the list heard nothing, and nothing said so. - Now a refused connection moves on. The hooks try each socket file in
the same order as before and send to the first one that answers. One
listener gets each signal, never several. - Your agent turn still doesn't wait. The whole attempt, however many
files it tries, still gives up after 50 ms and never raises an error. ASCENDA_LIVE_BUS_SOCKETis unchanged. Set it and that path is the
only one tried.- Nothing gets deleted. The hooks leave a stale socket file where it
is. Cleaning it up is the listener's job.
What's Changed
- fix(tool-kit): a stale live-bus socket no longer swallows every signal by @ascendaone-com in #121
Full Changelog: v0.1.26...v0.1.27
v0.1.26
When the agent stops and waits for you, that now counts
- Being interrupted is recorded as its own thing. Claude Code fires a
notification when it stops and waits for you: asking permission to run
something, or simply waiting for input. Codex does the same at its approval
gate. Neither was recorded before: both hooks were unregistered, so the
number of times an agent stopped and waited on you was always zero, and zero
looked exactly like never being interrupted. - Nothing about what was asked leaves your machine. The event carries one
field,interruptionKind, which ispermission_request,idle_promptor
other, and nothing else. Not the notification's wording, not the command awaiting
approval, not the path it touches, not your answer. A test in each adapter
fails the build if a future change adds any of them. - It rides the telemetry permission you already gave. No new consent to
grant, because producing it needs nothing that the tool-call and file events
covered by that permission didn't already need: a hook fires when the agent
reaches a gate, and no content is read to send it. otheris a real answer, not a leftover. These notifications get reworded
between releases, and a wording we don't recognise is reported asother
rather than guessed at. If that share climbs, the labels need updating. It
is meant to be visible.- Codex users merging hooks by hand:
examples/hooks.jsongains a
PermissionRequestentry.setupwrites it for you; a hand-merged file needs
the line added. - What this is not. A count of interruptions, and nothing built on top of
it. There is no score, no streak, and nothing that fires when a number moves.
A resumed session's minutes are its own
- Active time stops at the session boundary. Resuming or forking a Claude
Code session writes a transcript that opens with a copy of everything it
inherited. Those copied lines were counted twice: once in the session they
were typed in, once in every session resumed from it. v0.1.25 fixed the
prompt counts and left the minutes alone. They're fixed now. - Your session totals drop. Your week doesn't. On one 983-session store the
per-session active minutes summed to 70,212 and now sum to 53,600, while the
union of the same intervals moved by a single minute. Hands-on falls
furthest, 12,238 minutes to 8,006. Every minute of work is still in there,
counted once, where it happened. - A session starts when you started it.
startedAtwas the oldest
timestamp anywhere in the file, which on a resume is whenever the first
ancestor began. 127 of 978 sessions on that store now show a later start, 13
of them by more than an hour. - Counts of agents running at once get honest. Exact-duplicate spans were
30% of every active span in the store and are now 3.5%. A duplicated span
reads as a second agent, so any concurrency figure was partly reading copies. minutesBasis: "owned_lines"on the handoff says which minutes you're
reading."every_line", and a missing stamp, mean the old figures. Gate on
it before you compare two handoffs. The schema number stays where it is.- Resume a session, type nothing, and there's no session to report. The
file holds the copy and that's all, so the import counts it in
sessionsWithOnlyInheritedLinesand moves on. - Both importers agree. The desktop app reads by the same rule.
The runs you cut short are the ones you cut short
- A resume no longer reports its ancestors' interruptions. Pressing Escape
writes a marker into the transcript, and a resumed session copies that marker
along with the rest of the history it inherited — so the same interruption
was reported again by every session resumed from it. Only the session you
pressed Escape in counts it now. - Expect a lower count, on the same store. 191 counted runs became 151 on
one store of 990 transcripts: a fifth of them were replays. 26 sessions
changed and 19 now report none at all. Nothing real was dropped: every
marker is still counted somewhere, and the per-day figures move with the
totals. - The rule is the one the minutes already use. Same ownership, asked of a
third thing, so a session's cut-short count and its minutes now answer the
same question about the same lines. A cut whose own session has been purged
still counts, in the first transcript that holds it.
Minutes a second file also claimed
- Some copies rewrite the session id, and both files claimed the line. The
rule that stopped a resumed session counting its ancestors' minutes trusts
the id each line carries. Most copies keep it; some rewrite it to the
copying file's own name while keeping everything else, down to the original
timestamp — so both files said the line was theirs and both counted it. It
isn't a version quirk: the transcripts carrying it span fourteen Claude Code
releases. - Another 3.9% comes off your session totals, and your week still doesn't
move. On one store of 991 transcripts, 3.7% of the lines claiming their own
file were claimed by two. Summed active minutes fall 53,945 to 51,865 and
hands-on 8,052 to 7,599, while the union of the same intervals holds to a
minute, 25,248 to 25,247, which is what says a line was dropped and not a
minute of work. 42 sessions change and 34 now start later. - The minutes land on the session that did the work. The file that wrote a
line is the one still holding a line nobody else claims — its own history has
started there. Where that file has been purged, the first in walk order keeps
it, as with any orphan. Picking by walk order alone would have moved a third
of these lines onto the copy instead. - A prompt and the minute it happened in now agree about whose they are.
The prompt count never double-counted these, but it could put the prompt in
one session and its instant in another. - Imports take a little longer. The pre-read now reads every line's id
rather than only the lines a prompt could hide in, and the files holding a
contested id are read once more — 76 of 991 on that store. Measured end to
end: 24s to 30s.
Days you worked show up even when you typed nothing that session
- A session with no prompt of its own still gets its day breakdown. Sessions
opened from a chip have no typed prompt, and neither does a resume whose
prompts all belong to an ancestor, which v0.1.25 made common by counting
each typed prompt once. The per-day
slices stopped at the prompt list, so those sessions came back with no days
at all. Their session totals were right the whole time. Only the placement
went missing, which is why nothing looked wrong. - What comes back. On one 987-session store, 103 sessions had no prompt of
their own and every one of them reported an empty breakdown. Day-placed
active minutes go from 50,173 to 51,662, and five cut-short runs land on the
day they happened. - Codex imports gain the same days. Both extractors that measure active
time were affected. A store whose only timestamps are its prompts, Cursor
and VS Code, behaves as before. - Anything summing the day breakdown will read higher. The session figures
don't move, so a total built fromdays[]gets closer to the one built from
sessions.
What's Changed
- Count when the agent stops and waits for the person by @ascendaone-com in #115
- fix(history-import): a resumed session's minutes are its own by @ascendaone-com in #116
- fix(history-import): count a cut-short run in the session it was cut in by @ascendaone-com in #118
- fix(history-import): a line two files both claim belongs to the one that wrote it by @ascendaone-com in #119
- fix(history-import): place the days of a session nobody typed in by @ascendaone-com in #117
- docs(history-import): say where the ownership rule still misses by @ascendaone-com in #120
Full Changelog: v0.1.25...v0.1.26