@ctrl-spc/cs 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents.js +70 -5
- package/dist/autostart.js +15 -1
- package/dist/browser.js +386 -0
- package/dist/codebases.js +15 -0
- package/dist/codex-home.js +501 -0
- package/dist/companion.js +9 -12
- package/dist/config.js +23 -0
- package/dist/daemon.js +33 -3
- package/dist/env.js +42 -0
- package/dist/failure-reason.js +98 -0
- package/dist/index.js +1 -1
- package/dist/mcp.js +11571 -910
- package/dist/orchestrator.js +6011 -0
- package/dist/panel3/answer.js +166 -0
- package/dist/panel3/checkout.js +29 -0
- package/dist/panel3/cli.js +83 -0
- package/dist/panel3/client.js +181 -0
- package/dist/panel3/coordinator.js +18 -0
- package/dist/panel3/presence.js +162 -0
- package/dist/panel3/prompt.js +677 -0
- package/dist/panel3/run.js +1988 -0
- package/dist/panel3/say.js +262 -0
- package/dist/panel3/secrets.js +98 -0
- package/dist/panel3/show.js +996 -0
- package/dist/panel3/spawn.js +503 -0
- package/dist/panel3/tools.js +1601 -0
- package/dist/presence.js +178 -6
- package/dist/win-shell.js +162 -0
- package/dist/work-context.js +1484 -0
- package/package.json +3 -2
package/dist/env.js
CHANGED
|
@@ -10,6 +10,48 @@ export const SUPABASE_KEY = process.env.CTRL_SPC_SUPABASE_KEY || 'sb_publishable
|
|
|
10
10
|
export const HEARTBEAT_INTERVAL_MS = 10_000;
|
|
11
11
|
/** Command (ping) poll cadence. */
|
|
12
12
|
export const COMMAND_POLL_INTERVAL_MS = 3_000;
|
|
13
|
+
/** Orchestrator listener cadence (feature 16, Slice 3). The interval a designated
|
|
14
|
+
* machine waits before looking for a new loose todo — and therefore the worst
|
|
15
|
+
* case for ux.md mock state E step 1, "Working with no agent named. Present for
|
|
16
|
+
* at most one poll interval". Matched to the command poll: both are one small
|
|
17
|
+
* indexed read against the user's own rows, and a second cadence to reason
|
|
18
|
+
* about would buy nothing. Not faster — the panel is realtime, so the user sees
|
|
19
|
+
* their own todo appear instantly; this interval only bounds how long before an
|
|
20
|
+
* agent is NAMED on it. */
|
|
21
|
+
export const ORCHESTRATOR_POLL_INTERVAL_MS = 3_000;
|
|
22
|
+
/** !Cleanup Phase 7 Slice 1 (I21, I24) — HOW MANY AGENTS MAY RUN AT ONCE.
|
|
23
|
+
*
|
|
24
|
+
* Was structurally ONE (a boolean `busy` on the listener state), which is the
|
|
25
|
+
* defect behind the user's first requirement: a second request typed seconds
|
|
26
|
+
* after the first sat untouched until the first finished, for up to the whole
|
|
27
|
+
* spawn timeout.
|
|
28
|
+
*
|
|
29
|
+
* THREE, and bounded on purpose. Each run is a real headless `claude` process
|
|
30
|
+
* with real memory and real API cost, so a burst of twenty requests must not
|
|
31
|
+
* fork twenty agents. Three is enough that a question is never stuck behind a
|
|
32
|
+
* build — the case the user named — while staying comfortable on a laptop that
|
|
33
|
+
* is also running the user's editor and browser.
|
|
34
|
+
*
|
|
35
|
+
* Overridable per machine, because the right number on a 64 GB desktop is not
|
|
36
|
+
* the right number on a laptop.
|
|
37
|
+
*
|
|
38
|
+
* THE REAL CEILING TURNED OUT NOT TO BE THE MACHINE. The Phase 7 Slice 1 walk
|
|
39
|
+
* ran three concurrent Opus workers and two of them came back `claude exited 1`
|
|
40
|
+
* while the third answered normally — the agent binary was fine when run by
|
|
41
|
+
* hand, and the stream carried a `rate_limit_event` for the five-hour window.
|
|
42
|
+
* So the binding constraint on concurrency is the user's API rate limit, not
|
|
43
|
+
* RAM or CPU, and raising this number past what the account allows converts
|
|
44
|
+
* work that would have queued into work that FAILS and burns attempts. A
|
|
45
|
+
* rate-limited run is currently indistinguishable from a crashed one, which is
|
|
46
|
+
* worth fixing before this default goes any higher. */
|
|
47
|
+
export const ORCHESTRATOR_MAX_CONCURRENT = Number(process.env.CTRL_SPC_V2_MAX_CONCURRENT) || 3;
|
|
48
|
+
/** Hard ceiling on one headless agent run before it is killed and the todo is
|
|
49
|
+
* released for retry. Generous on purpose: the spawned agent does REAL WORK
|
|
50
|
+
* (ux.md § Purpose) — it may cut a worktree, run a build, read a codebase — so a
|
|
51
|
+
* short timeout would kill legitimate work and hand the user a released todo
|
|
52
|
+
* that keeps failing the same way. It exists only so a wedged process cannot
|
|
53
|
+
* hold the single in-flight slot forever. */
|
|
54
|
+
export const ORCHESTRATOR_SPAWN_TIMEOUT_MS = 30 * 60_000;
|
|
13
55
|
/** How long a work session stays "working" after the agent's last ctrl-spc tool
|
|
14
56
|
* call, with no further activity. The companion keeps last_seen_at fresh within
|
|
15
57
|
* this window; once it lapses (agent finished, Ctrl-C'd, crashed, or asleep) the
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 18a SLICE 5: WHAT THE USER READS WHEN AN ATTEMPT FAILS.
|
|
3
|
+
*
|
|
4
|
+
* .implementations/18-orchestrator/18a-you-answer-and-the-work-continues/ux.md
|
|
5
|
+
* § "Slice 5. A failure you did not cause reads like the machine"
|
|
6
|
+
*
|
|
7
|
+
* The daemon's internal error strings are exit statuses: `claude exited 1`,
|
|
8
|
+
* `claude exited ENOENT`, `codex produced no output`. They are the right thing
|
|
9
|
+
* to LOG, and they are what every existing test asserts on, so they are left
|
|
10
|
+
* exactly as they are. What was wrong is that the same string reached the card.
|
|
11
|
+
*
|
|
12
|
+
* An exit code is not an explanation. It is identical whether the binary is
|
|
13
|
+
* missing, the account is rate limited, or the agent genuinely crashed, and the
|
|
14
|
+
* user cannot act on any of them from the number. This translates at the point
|
|
15
|
+
* the reason is STORED, so the log keeps the diagnosis and the card gets the
|
|
16
|
+
* sentence.
|
|
17
|
+
*
|
|
18
|
+
* THE FALLBACK IS HONEST, not a guess. An error this does not recognise becomes
|
|
19
|
+
* "the agent stopped without finishing", which is true of every one of them, and
|
|
20
|
+
* the technical string is still in the daemon log for whoever needs it. Inventing
|
|
21
|
+
* a cause from an exit code would be the same defect wearing better words.
|
|
22
|
+
*/
|
|
23
|
+
/** How many times the orchestrator dispatches before giving up.
|
|
24
|
+
* `MAX_ATTEMPTS` in orchestrator.ts is the definition; this module does not
|
|
25
|
+
* need it, but the web app's copy of the number is documented against the same
|
|
26
|
+
* fact: it is deliberately not configurable, so a constant cannot drift into a
|
|
27
|
+
* lie the way a configurable one would. */
|
|
28
|
+
/**
|
|
29
|
+
* The agent's name as the user picked it in the machine popover.
|
|
30
|
+
*
|
|
31
|
+
* Shared rather than inlined because 18a Slice 7 composes its own sentence: the
|
|
32
|
+
* liveness watch does not have an error string to translate, it has a FACT (the
|
|
33
|
+
* process is gone), and a sentence written from that fact is more honest than
|
|
34
|
+
* one matched out of a string nobody wrote.
|
|
35
|
+
*
|
|
36
|
+
* An agent this does not know about is passed through rather than mangled.
|
|
37
|
+
*/
|
|
38
|
+
export function agentDisplayName(agent) {
|
|
39
|
+
return agent === 'claude' ? 'Claude' : agent === 'codex' ? 'Codex' : agent;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Plain words for a failed attempt, from the daemon's own error string plus
|
|
43
|
+
* whatever the child printed to stderr.
|
|
44
|
+
*
|
|
45
|
+
* `agent` is capitalised into the sentence because the user picked it by name
|
|
46
|
+
* ("Claude", "Codex") and a sentence about "the agent" reads as being about
|
|
47
|
+
* something they did not choose.
|
|
48
|
+
*/
|
|
49
|
+
export function plainFailureReason(agent, error, stderr = '') {
|
|
50
|
+
const name = agentDisplayName(agent);
|
|
51
|
+
const haystack = `${error}\n${stderr}`.toLowerCase();
|
|
52
|
+
/* THE COMMAND IS NOT THERE. `ENOENT` from spawn is the fixture the scenario
|
|
53
|
+
uses (the agent removed from PATH) and the single most likely real cause on
|
|
54
|
+
a fresh machine. It is also the one the user can fix in a minute, which is
|
|
55
|
+
why it is worth naming rather than folding into the fallback. */
|
|
56
|
+
if (haystack.includes('enoent') || haystack.includes('command not found')) {
|
|
57
|
+
return `${name} is not installed on this machine, or is not on its PATH.`;
|
|
58
|
+
}
|
|
59
|
+
/* RATE LIMITED. ux.md records the measurement this came from: on 2026-08-07
|
|
60
|
+
two of three concurrent runs died on the account's rate limit while the same
|
|
61
|
+
work succeeded alone. It is the reason the cap stays at three, and until now
|
|
62
|
+
it was indistinguishable from a crash. */
|
|
63
|
+
if (haystack.includes('rate limit') || haystack.includes('rate_limit')
|
|
64
|
+
|| haystack.includes('429') || haystack.includes('overloaded')) {
|
|
65
|
+
return `${name}'s usage limit is in effect on this account, so the run could not start.`;
|
|
66
|
+
}
|
|
67
|
+
/* SIGNED OUT. Recoverable by the user, and silent otherwise: the run just
|
|
68
|
+
fails, over and over, for as long as the session is stale.
|
|
69
|
+
|
|
70
|
+
18a SLICE 7 ADDED THE OAUTH AND 401 SIGNATURES, from the real thing rather
|
|
71
|
+
than from imagination. The Windows machine's Claude Code had a stale token
|
|
72
|
+
and said, on STDOUT as a stream-json result: `"error":
|
|
73
|
+
"authentication_failed"` and `API Error: 401 OAuth access token has
|
|
74
|
+
expired.` The word "authentication" above would have matched that, but only
|
|
75
|
+
once stdout was being read at all, which is the other half of the fix. `401`
|
|
76
|
+
and `oauth` are here so the same failure is caught when it arrives in
|
|
77
|
+
shorter words. */
|
|
78
|
+
if (haystack.includes('unauthorized') || haystack.includes('not logged in')
|
|
79
|
+
|| haystack.includes('authentication') || haystack.includes('oauth')
|
|
80
|
+
|| haystack.includes('401')) {
|
|
81
|
+
return `${name} is not signed in on this machine.`;
|
|
82
|
+
}
|
|
83
|
+
/* THE RUN WAS KILLED, by the timeout or by a signal from outside. Not the
|
|
84
|
+
user's Stop, which never reaches here: a stopped run is settled with its own
|
|
85
|
+
flag well before this. */
|
|
86
|
+
if (haystack.includes('timed out')) {
|
|
87
|
+
return `The run was stopped for taking too long.`;
|
|
88
|
+
}
|
|
89
|
+
if (haystack.includes('killed by')) {
|
|
90
|
+
return `${name} was killed on this machine before it finished.`;
|
|
91
|
+
}
|
|
92
|
+
/* IT RAN AND SAID NOTHING. A clean exit with no answer, which is a real state
|
|
93
|
+
and reads very differently from a crash. */
|
|
94
|
+
if (haystack.includes('produced no output')) {
|
|
95
|
+
return `${name} finished without producing an answer.`;
|
|
96
|
+
}
|
|
97
|
+
return `${name} stopped without finishing.`;
|
|
98
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ const HELP = `cs — CTRL+SPC
|
|
|
11
11
|
cs Open the Companion app (the front door)
|
|
12
12
|
cs open Open the Companion app in your browser
|
|
13
13
|
cs login Sign in from the terminal and link this computer
|
|
14
|
-
cs start Come online
|
|
14
|
+
cs start Come online and answer cards, no window (used by auto-start)
|
|
15
15
|
cs status Show sign-in state, computer, and detected agents
|
|
16
16
|
cs autostart on Come online automatically at login
|
|
17
17
|
cs autostart off Stop coming online at login
|