@agent-compose/sdk 0.7.0 → 0.8.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/README.md +66 -39
- package/dist/agent/__tests__/runtime-json-schema.test.d.ts +10 -0
- package/dist/agent/agent-context.d.ts +21 -1
- package/dist/agent/agent-loop.d.ts +24 -1
- package/dist/client.d.ts +338 -534
- package/dist/directives.d.ts +112 -0
- package/dist/display.d.ts +242 -0
- package/dist/errors.d.ts +24 -1
- package/dist/index.d.ts +24 -12
- package/dist/index.js +3545 -1667
- package/dist/pause/wrappers.d.ts +31 -9
- package/dist/runtimes/_acp-client.d.ts +46 -1
- package/dist/runtimes/_cli-agent.d.ts +49 -4
- package/dist/runtimes/_jsonl-guard.d.ts +103 -0
- package/dist/runtimes/amp.d.ts +2 -2
- package/dist/runtimes/claude-code.d.ts +59 -0
- package/dist/runtimes/claude-code.test.d.ts +14 -0
- package/dist/runtimes/claude.d.ts +16 -0
- package/dist/runtimes/claude.test.d.ts +8 -0
- package/dist/runtimes/codex.d.ts +9 -3
- package/dist/runtimes/cursor.d.ts +2 -2
- package/dist/runtimes/droid.d.ts +2 -2
- package/dist/runtimes/jsonl-guard.test.d.ts +19 -0
- package/dist/runtimes/openai-desktop.js +2691 -864
- package/dist/runtimes/opencode.d.ts +2 -2
- package/dist/runtimes/vercel.js +12 -1
- package/dist/sandbox/devbox.d.ts +42 -0
- package/dist/sandbox/exec-stream.d.ts +14 -0
- package/dist/sandbox/network-policy.d.ts +100 -0
- package/dist/sandbox/provider-def.d.ts +79 -0
- package/dist/sandbox/providers/desktop.d.ts +10 -0
- package/dist/sandbox/providers/e2b.d.ts +17 -0
- package/dist/sandbox/providers/local.d.ts +11 -0
- package/dist/sandbox/providers/vercel.d.ts +18 -0
- package/dist/sandbox/registry.d.ts +45 -0
- package/dist/sandbox/sizes.d.ts +68 -0
- package/dist/sandbox.d.ts +24 -299
- package/dist/step-invocation/__tests__/foreground-recovery.test.d.ts +1 -0
- package/dist/step-invocation/invoker.d.ts +10 -0
- package/dist/step-invocation/protocol.d.ts +5 -0
- package/dist/types/api-compliance.d.ts +71 -0
- package/dist/types/api-conversations.d.ts +492 -0
- package/dist/types/api-factory.d.ts +309 -0
- package/dist/types/api-projects.d.ts +131 -0
- package/dist/types/api-runs.d.ts +377 -0
- package/dist/types/api-scopes.d.ts +102 -0
- package/dist/types/conversation-stream.d.ts +191 -0
- package/dist/types/execution-context.d.ts +12 -2
- package/dist/types/protocol.d.ts +30 -1
- package/dist/types/sandbox-environment.d.ts +8 -5
- package/dist/types/sandbox.d.ts +74 -4
- package/dist/types/workflow-metadata.d.ts +33 -8
- package/dist/types/workflow-plan.d.ts +10 -0
- package/dist/types/workflow.d.ts +18 -205
- package/dist/utils/bundler.d.ts +12 -1
- package/dist/workflow-steps/index.d.ts +1 -1
- package/dist/workflow-steps/observability.d.ts +8 -1
- package/dist/workflow-steps/runner.d.ts +3 -3
- package/dist/workflow-steps/step.d.ts +15 -1
- package/dist/workflow-steps/types.d.ts +19 -5
- package/dist/workflow-steps/workflow.d.ts +22 -1
- package/dist/workflows/engine.d.ts +3 -2
- package/dist/workflows/invoke-child.d.ts +2 -2
- package/package.json +1 -1
- package/src/agent/agent-context.ts +186 -3
- package/src/agent/agent-loop.ts +31 -2
- package/src/client.ts +909 -621
- package/src/directives.ts +184 -0
- package/src/display.ts +788 -0
- package/src/errors.ts +39 -0
- package/src/index.ts +104 -10
- package/src/pause/wrappers.ts +44 -9
- package/src/runtimes/_acp-client.ts +72 -3
- package/src/runtimes/_cli-agent.ts +159 -36
- package/src/runtimes/_jsonl-guard.ts +219 -0
- package/src/runtimes/claude-code.ts +246 -0
- package/src/runtimes/claude.ts +32 -2
- package/src/runtimes/codex.ts +55 -3
- package/src/runtimes/openai-desktop.ts +59 -14
- package/src/sandbox/devbox.ts +48 -0
- package/src/sandbox/exec-stream.ts +48 -0
- package/src/sandbox/network-policy.ts +181 -0
- package/src/sandbox/provider-def.ts +94 -0
- package/src/sandbox/providers/desktop.ts +57 -0
- package/src/sandbox/providers/e2b.ts +354 -0
- package/src/sandbox/providers/local.ts +106 -0
- package/src/sandbox/providers/vercel.ts +331 -0
- package/src/sandbox/registry.ts +198 -0
- package/src/sandbox/sizes.ts +95 -0
- package/src/sandbox.ts +59 -1275
- package/src/step-invocation/invoker.ts +151 -28
- package/src/step-invocation/protocol.ts +8 -0
- package/src/types/api-compliance.ts +79 -0
- package/src/types/api-conversations.ts +522 -0
- package/src/types/api-factory.ts +336 -0
- package/src/types/api-projects.ts +140 -0
- package/src/types/api-runs.ts +412 -0
- package/src/types/api-scopes.ts +102 -0
- package/src/types/conversation-stream.ts +231 -0
- package/src/types/execution-context.ts +10 -2
- package/src/types/protocol.ts +33 -0
- package/src/types/sandbox-environment.ts +28 -9
- package/src/types/sandbox.ts +73 -4
- package/src/types/workflow-metadata.ts +35 -8
- package/src/types/workflow-plan.ts +11 -0
- package/src/types/workflow.ts +25 -292
- package/src/utils/bundler.ts +32 -5
- package/src/utils/errors.ts +16 -1
- package/src/workflow-steps/index.ts +1 -0
- package/src/workflow-steps/observability.ts +19 -8
- package/src/workflow-steps/runner.ts +4 -4
- package/src/workflow-steps/step.ts +49 -1
- package/src/workflow-steps/types.ts +20 -5
- package/src/workflow-steps/workflow.ts +22 -1
- package/src/workflows/engine.ts +3 -2
- package/src/workflows/invoke-child.ts +2 -2
|
@@ -7,19 +7,33 @@
|
|
|
7
7
|
* - `output` Zod schema; validated against `run`'s return value
|
|
8
8
|
* - `run` step body
|
|
9
9
|
*
|
|
10
|
+
* Optional narrative fields (rendered on the dashboard workflow graph):
|
|
11
|
+
* - `summary` one plain sentence of what the step actually does
|
|
12
|
+
* - `deliverables` files the step promises to produce
|
|
13
|
+
*
|
|
10
14
|
* Validation is required (not optional) because the durability story rides
|
|
11
15
|
* on every step boundary being a recordable, replayable JSON value. A step
|
|
12
16
|
* without a schema is invisible to the engine's persistence layer.
|
|
13
17
|
*
|
|
18
|
+
* Narrative caps are enforced HERE, at construction — the bundler evaluates
|
|
19
|
+
* the module at registration, so this is the "length-capped at registration"
|
|
20
|
+
* point and the error names the step in the author's own environment. The
|
|
21
|
+
* server's manifest zod re-checks the same caps (untrusted input).
|
|
22
|
+
*
|
|
14
23
|
* Type inference flows: `defineStep` infers TInput/TOutput from the schemas
|
|
15
24
|
* so `run(ctx)` is fully typed via `ctx.input` at the call site.
|
|
16
25
|
*/
|
|
17
26
|
import type { z } from "zod";
|
|
18
|
-
import type { Step, StepContext } from "./types.js";
|
|
27
|
+
import type { Step, StepContext, StepDeliverable } from "./types.js";
|
|
19
28
|
export interface DefineStepOpts<TInput, TOutput> {
|
|
20
29
|
name: string;
|
|
21
30
|
input: z.ZodType<TInput>;
|
|
22
31
|
output: z.ZodType<TOutput>;
|
|
23
32
|
run(ctx: StepContext<TInput>): TOutput | Promise<TOutput>;
|
|
33
|
+
/** One plain sentence of what the step actually does. 1-200 chars, no
|
|
34
|
+
* control characters (so no newlines). */
|
|
35
|
+
summary?: string;
|
|
36
|
+
/** Files the step promises to produce. At most 8. */
|
|
37
|
+
deliverables?: StepDeliverable[];
|
|
24
38
|
}
|
|
25
39
|
export declare function defineStep<TInput, TOutput>(opts: DefineStepOpts<TInput, TOutput>): Step<TInput, TOutput>;
|
|
@@ -14,6 +14,7 @@ import type { z } from "zod";
|
|
|
14
14
|
import type { BaseExecutionContext } from "../types/execution-context.js";
|
|
15
15
|
import type { AgentEventSink } from "../types/workflow.js";
|
|
16
16
|
import type { WorkflowMetadata } from "../types/workflow-metadata.js";
|
|
17
|
+
import type { StepObservability } from "./observability.js";
|
|
17
18
|
/**
|
|
18
19
|
* Per-step execution context. Threaded into every step's `execute(...)` so
|
|
19
20
|
* the step can read tenant identity and run identity, log progress, and
|
|
@@ -59,6 +60,14 @@ export interface StepContext<TInput = unknown> extends BaseExecutionContext {
|
|
|
59
60
|
*/
|
|
60
61
|
agentEvents: AgentEventSink;
|
|
61
62
|
}
|
|
63
|
+
/** One artifact a step promises to produce. `path` is workspace/drive-relative
|
|
64
|
+
* (e.g. "out/report.html"); the dashboard derives the format tag from the
|
|
65
|
+
* extension client-side — no `format` field here. */
|
|
66
|
+
export interface StepDeliverable {
|
|
67
|
+
path: string;
|
|
68
|
+
/** Optional one-line description of the artifact. */
|
|
69
|
+
description?: string;
|
|
70
|
+
}
|
|
62
71
|
/**
|
|
63
72
|
* Step definition — a single typed unit of work in a workflow chain.
|
|
64
73
|
*
|
|
@@ -77,6 +86,11 @@ export interface Step<TInput, TOutput> {
|
|
|
77
86
|
readonly output: z.ZodType<TOutput>;
|
|
78
87
|
/** Step body. Receives a `StepContext<TInput>` and returns the typed output. */
|
|
79
88
|
run(ctx: StepContext<TInput>): TOutput | Promise<TOutput>;
|
|
89
|
+
/** One plain sentence of what the step actually does — rendered on the
|
|
90
|
+
* dashboard workflow graph. ≤200 chars, no newlines. */
|
|
91
|
+
readonly summary?: string;
|
|
92
|
+
/** Files the step promises to produce. ≤8 entries. */
|
|
93
|
+
readonly deliverables?: readonly StepDeliverable[];
|
|
80
94
|
}
|
|
81
95
|
/**
|
|
82
96
|
* The result of running one step. Engine adapters persist these into the
|
|
@@ -89,18 +103,18 @@ export type StepRunResult<TOutput = unknown> = {
|
|
|
89
103
|
status: "completed";
|
|
90
104
|
output: TOutput;
|
|
91
105
|
durationMs: number;
|
|
92
|
-
observability?:
|
|
106
|
+
observability?: StepObservability;
|
|
93
107
|
} | {
|
|
94
108
|
status: "failed";
|
|
95
109
|
error: string;
|
|
96
110
|
durationMs: number;
|
|
97
|
-
observability?:
|
|
111
|
+
observability?: StepObservability;
|
|
98
112
|
};
|
|
99
113
|
/**
|
|
100
114
|
* Workflow — a list of typed steps plus the workflow's input/output
|
|
101
|
-
* schemas plus its server-side metadata bag. Returned by
|
|
102
|
-
*
|
|
103
|
-
*
|
|
115
|
+
* schemas plus its server-side metadata bag. Returned by
|
|
116
|
+
* `defineWorkflow(...).step(...)...build()`. Engine adapters consume
|
|
117
|
+
* this shape.
|
|
104
118
|
*
|
|
105
119
|
* `input` validates the workflow input before the first step runs.
|
|
106
120
|
* `output` validates the final step's output before the workflow
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import type { z } from "zod";
|
|
22
22
|
import type { Step, Workflow } from "./types.js";
|
|
23
|
-
import type { SnapshotConfig, SandboxResources } from "../types/workflow-metadata.js";
|
|
23
|
+
import type { SnapshotConfig, SandboxResources, ConnectorRequirements, ConnectorOperationTag, InvokePolicy, DriveMergePolicy } from "../types/workflow-metadata.js";
|
|
24
24
|
import type { SandboxNetworkPolicy } from "../sandbox.js";
|
|
25
25
|
import type { Processor } from "../processors/processor.js";
|
|
26
26
|
export interface WorkflowBuilder<TInput, TCurrent> {
|
|
@@ -50,7 +50,28 @@ export interface StepWorkflowDefinition<TInput, TOutput> {
|
|
|
50
50
|
* and `provider` (`vercel` | `e2b`). Vercel maps `size` to vCPUs; E2B
|
|
51
51
|
* sizing is template-defined. Omit → smallest SKU on the default provider. */
|
|
52
52
|
resources?: SandboxResources;
|
|
53
|
+
/** What happens to this workflow's drive branch when a run ends:
|
|
54
|
+
* `"auto"` (the default when omitted — today's behaviour) folds it into
|
|
55
|
+
* `main`; `"manual"` proposes a merge approval at the same terminus
|
|
56
|
+
* instead, leaving the branch durable until a human clicks approve. A
|
|
57
|
+
* cancelled run neither merges nor proposes under either policy. */
|
|
58
|
+
mergePolicy?: DriveMergePolicy;
|
|
53
59
|
processors?: readonly Processor[];
|
|
60
|
+
/** Connector requirements (ADR-0007) — providers whose APIs this workflow
|
|
61
|
+
* calls. Dispatch resolves an authorized grant per provider and injects a
|
|
62
|
+
* fresh access token at the network layer. */
|
|
63
|
+
connectors?: ConnectorRequirements;
|
|
64
|
+
/** Marks this workflow as a catalogue OPERATION of a connector — e.g. the
|
|
65
|
+
* `create-issue` operation of the `github` connector. */
|
|
66
|
+
connectorOperation?: ConnectorOperationTag;
|
|
67
|
+
/** Tier-1 invoke ACL — who may dispatch this connector-brokering workflow.
|
|
68
|
+
* See `InvokePolicy`. */
|
|
69
|
+
invokePolicy?: InvokePolicy;
|
|
70
|
+
/** Internal — set by `defineSandboxEnvironment`, not by workflow authors.
|
|
71
|
+
* Marks the workflow as an environment build (base-env / agent-env) so the
|
|
72
|
+
* server skips mounting the shared factory drive for its runs (#13). See
|
|
73
|
+
* `WorkflowMetadata.environmentBuild`. */
|
|
74
|
+
environmentBuild?: boolean;
|
|
54
75
|
}
|
|
55
76
|
export declare function createStepWorkflow<TInput, TOutput>(opts: StepWorkflowDefinition<TInput, TOutput>): WorkflowBuilder<TInput, TInput>;
|
|
56
77
|
/** Type guard — true when `value` is a `Workflow`. */
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
* Errors classified into `WorkflowError` (user code threw) vs `EngineError`
|
|
9
9
|
* (platform problem) for the runner harness to surface upstream.
|
|
10
10
|
*/
|
|
11
|
-
import type { WorkflowHooks
|
|
11
|
+
import type { WorkflowHooks } from "../types/workflow.js";
|
|
12
|
+
import type { InvokeChild } from "../types/execution-context.js";
|
|
12
13
|
import { RequestContext } from "../request-context/request-context.js";
|
|
13
14
|
import type { Workflow } from "../workflow-steps/types.js";
|
|
14
15
|
import { type RunWorkflowStepsOpts } from "../workflow-steps/runner.js";
|
|
@@ -54,7 +55,7 @@ export interface RunWorkflowOptions {
|
|
|
54
55
|
/** Provider-specific child workflow invocation. Temporal/Inngest providers
|
|
55
56
|
* inject their native child-workflow primitive; the LocalProvider injects
|
|
56
57
|
* the public Agent Compose API client. */
|
|
57
|
-
invokeChild?:
|
|
58
|
+
invokeChild?: InvokeChild;
|
|
58
59
|
}
|
|
59
60
|
export declare function runWorkflow<TInput, TOutput>(wf: Workflow<TInput, TOutput>, ctx: {
|
|
60
61
|
run: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { InvokeChild } from "../types/execution-context.js";
|
|
2
2
|
/**
|
|
3
3
|
* Build the public-API child workflow invoker used by legacy and sandboxed
|
|
4
4
|
* workflow execution. Provider-backed engines may inject a different
|
|
@@ -7,4 +7,4 @@ import type { WorkflowCtx } from "../types/workflow.js";
|
|
|
7
7
|
export declare function buildInvokeChild(runId: string, opts?: {
|
|
8
8
|
fallbackBaseUrl?: string;
|
|
9
9
|
defaultFactorySlug?: string;
|
|
10
|
-
}):
|
|
10
|
+
}): InvokeChild;
|
package/package.json
CHANGED
|
@@ -77,9 +77,9 @@ step) instead of writing source from memory — the skill scaffolds the correct,
|
|
|
77
77
|
current shape. Then \`agentc register <file.ts>\` (or \`/ac:register\`).
|
|
78
78
|
|
|
79
79
|
The skill writes **step-form** (a builder of discrete, durable \`.step()\`s).
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
The legacy run-form (\`defineWorkflow({ run(ctx, sandbox) { … } })\`) has been
|
|
81
|
+
REMOVED from the SDK — registering one fails with an error. Step-form is the
|
|
82
|
+
only shape: durable per-step replay, and pause only works there.
|
|
83
83
|
|
|
84
84
|
## Pausing to ask the human
|
|
85
85
|
|
|
@@ -125,14 +125,197 @@ request **without** an Authorization header and the platform adds it. Don't try
|
|
|
125
125
|
to read or exfiltrate tokens; they aren't here. The "Connectors & access"
|
|
126
126
|
section below (when present) lists exactly which providers this run can reach.
|
|
127
127
|
|
|
128
|
+
## Computer Use — you have a real desktop, and it is already running
|
|
129
|
+
|
|
130
|
+
**This machine has a graphical desktop.** Every session machine does — terminal
|
|
131
|
+
sessions included — and the platform brings it UP AT BOOT, before your first
|
|
132
|
+
turn: an X server on \`DISPLAY=:0\`, the openbox window manager, wallpaper and a
|
|
133
|
+
panel. You do not start it, you do not wait for a human to open it, and you do
|
|
134
|
+
not need a viewer. Go straight to driving it.
|
|
135
|
+
|
|
136
|
+
(The one exception, and it is rare: an image built without the GUI stack has no
|
|
137
|
+
display at all, and \`DISPLAY=:0 xdotool getdisplaygeometry\` errors outright.
|
|
138
|
+
That single case is the only one where this section does not apply — a
|
|
139
|
+
screenshot showing only wallpaper is NOT it, and neither is an app that failed
|
|
140
|
+
to start.)
|
|
141
|
+
|
|
142
|
+
**This is how you SEE anything.** Any question of the form "does it render?",
|
|
143
|
+
"is the page actually working?", "did the markers show up?", "what does it look
|
|
144
|
+
like?" is answered by opening it on this desktop and screenshotting it — not by
|
|
145
|
+
reasoning about the code, and not by a headless render (which proves the process
|
|
146
|
+
starts, not that the thing draws). Verify visually before you report visually.
|
|
147
|
+
|
|
148
|
+
- **Input** — \`xdotool\` against \`DISPLAY=:0\`: \`DISPLAY=:0 xdotool mousemove <x> <y>\`,
|
|
149
|
+
\`DISPLAY=:0 xdotool click 1\` (1=left, 3=right), \`DISPLAY=:0 xdotool type 'text'\`,
|
|
150
|
+
\`DISPLAY=:0 xdotool key Return\` (also \`ctrl+c\`, \`Tab\`, \`super\`, …).
|
|
151
|
+
- **Screenshots** — \`scrot\` (or ImageMagick's \`import\`):
|
|
152
|
+
\`DISPLAY=:0 scrot /tmp/screen.png\`, then READ the PNG to see the screen,
|
|
153
|
+
before and after you act. A screenshot is your only eyes here.
|
|
154
|
+
- **Apps + windows** — a plain X session. Launch in the background:
|
|
155
|
+
\`DISPLAY=:0 <app> &\`. Two things that trip agents up, both normal:
|
|
156
|
+
- a GUI app needs a **beat to map its window** — screenshot, and if you see
|
|
157
|
+
only wallpaper, wait a couple of seconds and screenshot again before
|
|
158
|
+
concluding anything;
|
|
159
|
+
- **Chromium needs \`--no-sandbox\`** in this environment (nested sandbox).
|
|
160
|
+
The whole recipe for looking at a local page:
|
|
161
|
+
\`DISPLAY=:0 chromium --no-sandbox --disable-gpu --start-maximized <url> &\`
|
|
162
|
+
then \`sleep 5\`, then \`DISPLAY=:0 scrot /tmp/screen.png\` and read it.
|
|
163
|
+
If a window still never appears, read the app's own log (\`/tmp/*.log\`) — the
|
|
164
|
+
desktop is not the thing that failed. Do NOT abandon it for a headless
|
|
165
|
+
screenshot: headless cannot tell you what the human will see.
|
|
166
|
+
- **A human can watch** — the session header carries a **Desktop** button in the
|
|
167
|
+
dashboard, and what a teammate sees there is exactly this display. The desktop
|
|
168
|
+
runs whether or not anyone is looking; never wait for a viewer.
|
|
169
|
+
|
|
170
|
+
Nothing here changes the credentials rule above: tokens are injected at the
|
|
171
|
+
network layer, never present on the desktop or in any file you can read — so
|
|
172
|
+
there is nothing to type, paste, or screenshot a credential from.
|
|
173
|
+
|
|
174
|
+
## Recording a demo — the desktop, captured to a video the human can play
|
|
175
|
+
|
|
176
|
+
"Record a demo of you using X" is a normal ask, and this machine does it:
|
|
177
|
+
start a screen recording, drive the app with \`xdotool\` exactly as in Computer
|
|
178
|
+
Use, stop the recording, and report the file. (For a LIVE view no recording is
|
|
179
|
+
needed — the session header's **Desktop** button already streams this display
|
|
180
|
+
to any teammate watching; a recording is the durable, replayable artifact.
|
|
181
|
+
Both modes exist; say so when it matters.)
|
|
182
|
+
|
|
183
|
+
**ffmpeg is NOT pre-installed** — install it first, once per machine:
|
|
184
|
+
|
|
185
|
+
sudo apt-get update -q && sudo apt-get install -y -q ffmpeg
|
|
186
|
+
|
|
187
|
+
(drop \`sudo\` if you are already root). Then the whole recipe:
|
|
188
|
+
|
|
189
|
+
DISPLAY=:0 ffmpeg -f x11grab \\
|
|
190
|
+
-video_size "$(DISPLAY=:0 xdotool getdisplaygeometry | tr ' ' x)" \\
|
|
191
|
+
-framerate 10 -i :0 -c:v libvpx -b:v 1M -deadline realtime -cpu-used 8 \\
|
|
192
|
+
demo.webm &
|
|
193
|
+
FFMPEG_PID=$!
|
|
194
|
+
# ... drive the app with xdotool, screenshotting as you go ...
|
|
195
|
+
kill -INT "$FFMPEG_PID" && wait "$FFMPEG_PID"
|
|
196
|
+
|
|
197
|
+
The gotchas, each one earned:
|
|
198
|
+
- **Stop with SIGINT (\`kill -INT\`), never SIGKILL** — ffmpeg finalizes the
|
|
199
|
+
file on SIGINT; a hard kill truncates the encode mid-write.
|
|
200
|
+
- **Record WebM (matroska-family), not MP4** — mp4 writes its moov atom at the
|
|
201
|
+
END, so a killed or crashed encode leaves an UNPLAYABLE file; webm stays
|
|
202
|
+
playable up to the last written frame and plays natively in the browser.
|
|
203
|
+
MP4's only edge is compatibility with some external players — transcode
|
|
204
|
+
afterwards if you truly need it, never record straight to it.
|
|
205
|
+
- **\`-video_size\` must match the real screen** — x11grab does not default to
|
|
206
|
+
it; read the geometry from \`xdotool getdisplaygeometry\` as above.
|
|
207
|
+
- **10–12 fps is right for a screen demo** — small files, legible UI motion;
|
|
208
|
+
this is not video production.
|
|
209
|
+
- **Write to the drive, not /tmp** — the recording must land in your working
|
|
210
|
+
directory to persist and show up in Files; a file in /tmp dies with the
|
|
211
|
+
sandbox.
|
|
212
|
+
- When you stop, **TELL the human the exact drive path** of the video — a
|
|
213
|
+
recording they cannot find might as well not exist.
|
|
214
|
+
|
|
215
|
+
## Previews — register every server you serve (cloud sessions)
|
|
216
|
+
|
|
217
|
+
In a cloud session, a dev server listening on a port becomes a hosted,
|
|
218
|
+
member-gated URL the human can open — but ONLY if you register it:
|
|
219
|
+
|
|
220
|
+
agentc preview open <port> [--name <label>] [--path </landing>]
|
|
221
|
+
# hosted URL + an "Open preview" card
|
|
222
|
+
agentc preview list # the registry — what is live right now
|
|
223
|
+
agentc preview close <port> # take one down
|
|
224
|
+
|
|
225
|
+
(\`agentc preview announce\` is the same verb as \`open\` — announce what you
|
|
226
|
+
serve.) \`--name\` is the human-readable label; \`--path\` is where the app
|
|
227
|
+
should open (e.g. \`/dashboard\`) — the card and every chip land the human
|
|
228
|
+
there instead of a bare \`/\`.
|
|
229
|
+
|
|
230
|
+
Register EVERY server you start for a human, the moment it is listening, and
|
|
231
|
+
tell them the URL the command printed. The registry is the only discoverable
|
|
232
|
+
record of what this machine serves: an unregistered server keeps running, but
|
|
233
|
+
nobody — not the human, not the assistant — can find its URL, and when the
|
|
234
|
+
sandbox recycles it is gone without a trace. Never guess or hand out a raw
|
|
235
|
+
port; the hosted URL from \`agentc preview open\` is the only address that
|
|
236
|
+
works outside this machine. (Outside a cloud session the command errors
|
|
237
|
+
honestly — there is no session sandbox to expose.)
|
|
238
|
+
|
|
239
|
+
What registration buys you: the human sees each registered preview as a card
|
|
240
|
+
in the conversation and a row in the session's Previews menu — MANY at once,
|
|
241
|
+
one per port — and the assistant resolves "open the preview" from this same
|
|
242
|
+
registry (its \`list_previews\` read), so what you register is exactly what
|
|
243
|
+
gets opened. On deployments with subdomain previews the hosted URL is a real
|
|
244
|
+
origin of its own — absolute asset paths and client-side routing work, the
|
|
245
|
+
whole app is navigable — so serve normally and let the platform address it;
|
|
246
|
+
never rewrite your app to a path prefix.
|
|
247
|
+
|
|
128
248
|
## Tools in this environment
|
|
129
249
|
|
|
130
250
|
- \`agentc\` — Agent Compose CLI (your primary interface; authed from env)
|
|
131
251
|
- \`@agent-compose/sdk\` — installed in /workspace for writing workflows
|
|
132
252
|
- \`/ac:*\` Claude Code skills — slash commands for the above
|
|
133
253
|
- \`archil\` (factory drive), \`rtk\`, \`bun\`
|
|
254
|
+
- \`xdotool\` / \`scrot\` — drive + screenshot the desktop (if this machine has one; see Computer Use)
|
|
134
255
|
- A world-writable \`/workspace\` working directory`;
|
|
135
256
|
|
|
257
|
+
/** Parameters for the `agentc session add` education brief (ADR-0055 §8). */
|
|
258
|
+
export interface AddedSessionBriefParams {
|
|
259
|
+
conversationId: string;
|
|
260
|
+
serverUrl: string;
|
|
261
|
+
dashboardUrl: string | null;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* The education brief `agentc session add` writes into a connected LOCAL
|
|
266
|
+
* session's CLAUDE.md/AGENTS.md — the same content family as
|
|
267
|
+
* `AGENT_COMPOSE_MANUAL`, rendered for the local context (one source,
|
|
268
|
+
* rendered per context). The manual above must stay BYTE-IDENTICAL (the
|
|
269
|
+
* server and base-env bake static copies of it); this function renders a
|
|
270
|
+
* sibling document and never touches it. The local context differs from the
|
|
271
|
+
* sandbox in exactly the ways stated here: auth rides the bridge credential
|
|
272
|
+
* fallback instead of injected run env; no factory drive is mounted — the
|
|
273
|
+
* files on this machine belong to the human; and the bound conversation is
|
|
274
|
+
* MIRROR-ONLY (ADR-0055 §8.6) — teammates read along but can never message
|
|
275
|
+
* the session through it, so the brief must not promise an inbound channel.
|
|
276
|
+
*/
|
|
277
|
+
export function buildAddedSessionBrief(p: AddedSessionBriefParams): string {
|
|
278
|
+
const dashboardSection = p.dashboardUrl === null ? "" : `
|
|
279
|
+
|
|
280
|
+
## Dashboard
|
|
281
|
+
|
|
282
|
+
Teammates follow this conversation (and the rest of the factory) in the
|
|
283
|
+
Agent Compose dashboard: ${p.dashboardUrl}`;
|
|
284
|
+
|
|
285
|
+
return `# Connected to Agent Compose
|
|
286
|
+
|
|
287
|
+
Agent Compose is your team's agent platform: durable conversations,
|
|
288
|
+
workflow runs, and shared factory drives where humans and agents work
|
|
289
|
+
together. THIS terminal's claude-code session is connected to Agent
|
|
290
|
+
Compose conversation \`${p.conversationId}\` on ${p.serverUrl}.
|
|
291
|
+
|
|
292
|
+
That conversation is a LIVE, READ-ONLY MIRROR of this terminal session:
|
|
293
|
+
teammates read along in the dashboard as the work happens, but they cannot
|
|
294
|
+
message you through it — anything posted there is answered by the server
|
|
295
|
+
with a notice and never reaches this terminal. Everything you do here is
|
|
296
|
+
mirrored automatically; you have an audience, not a channel.
|
|
297
|
+
|
|
298
|
+
## The \`agentc\` toolbelt
|
|
299
|
+
|
|
300
|
+
The \`agentc\` CLI works from this shell. It is already authenticated on
|
|
301
|
+
this machine via the bridge credential fallback — no keys to manage,
|
|
302
|
+
commands just work:
|
|
303
|
+
|
|
304
|
+
agentc list # registered workflows
|
|
305
|
+
agentc logs <run-id> # a run's logs
|
|
306
|
+
agentc invoke <workflow> -i '<json>' # dispatch a workflow
|
|
307
|
+
agentc events list # read the factory timeline
|
|
308
|
+
|
|
309
|
+
## Scope — this is your LOCAL machine
|
|
310
|
+
|
|
311
|
+
The files here are YOURS: no factory drive is mounted in this session,
|
|
312
|
+
and nothing you write locally lands on a shared drive by itself. Cloud
|
|
313
|
+
drive/branch semantics (per-run directories on the factory drive, drive
|
|
314
|
+
branches, persist-by-default outputs) apply only to cloud sessions —
|
|
315
|
+
not here.${dashboardSection}
|
|
316
|
+
`;
|
|
317
|
+
}
|
|
318
|
+
|
|
136
319
|
/**
|
|
137
320
|
* One connector this run can reach, as the agent should see it. Strictly
|
|
138
321
|
* NON-SECRET — hosts, methods, paths, identity only. The access token is
|
package/src/agent/agent-loop.ts
CHANGED
|
@@ -39,6 +39,24 @@ export function parseAgentStatus(text: string): AgentStatus | null {
|
|
|
39
39
|
|
|
40
40
|
const DEFAULT_ALLOWED_TOOLS = ["Read", "Write", "Edit", "Bash", "Glob", "Grep", "WebFetch"];
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* A `responseSchema` rendered for the runtime's structured-output surface.
|
|
44
|
+
*
|
|
45
|
+
* zod v4's `toJSONSchema` stamps `$schema: "…/draft/2020-12/schema"` on the
|
|
46
|
+
* result. Claude Code validates `--json-schema` with a validator that has no
|
|
47
|
+
* 2020-12 meta-schema registered, so any schema CARRYING that header is
|
|
48
|
+
* rejected at CLI startup — the process exits 1 before its first API call
|
|
49
|
+
* and every agent with a responseSchema dies on spawn (observed live on
|
|
50
|
+
* claude 2.1.212: `--json-schema is not a valid JSON Schema: no schema with
|
|
51
|
+
* key or ref "https://json-schema.org/draft/2020-12/schema"`). The header is
|
|
52
|
+
* pure metadata — drop it; the schema body is draft-07-compatible for every
|
|
53
|
+
* shape zod emits from our workflow schemas.
|
|
54
|
+
*/
|
|
55
|
+
export function runtimeJsonSchema(schema: z.ZodType<unknown>): Record<string, unknown> {
|
|
56
|
+
const { $schema: _$schema, ...rest } = z.toJSONSchema(schema) as Record<string, unknown>;
|
|
57
|
+
return rest;
|
|
58
|
+
}
|
|
59
|
+
|
|
42
60
|
export interface AgentLoopResult<TResponse = unknown> {
|
|
43
61
|
agentId: string;
|
|
44
62
|
label: string;
|
|
@@ -71,7 +89,12 @@ function preview(value: unknown): string {
|
|
|
71
89
|
}
|
|
72
90
|
}
|
|
73
91
|
|
|
74
|
-
|
|
92
|
+
/** Everything but the live-only streaming chunk: `text_delta` never becomes
|
|
93
|
+
* an agent.message event (the terminating `text` carries the whole block) —
|
|
94
|
+
* the loop filters it before summarizing. */
|
|
95
|
+
type DurableAgentMessage = Exclude<AgentMessage, { type: "text_delta" } | { type: "usage_delta" }>;
|
|
96
|
+
|
|
97
|
+
export function summarizeAgentMessage(msg: DurableAgentMessage): AgentMessageSummary {
|
|
75
98
|
switch (msg.type) {
|
|
76
99
|
case "init": return { type: "init", sessionId: msg.sessionId };
|
|
77
100
|
case "text": return { type: "text", text: msg.text };
|
|
@@ -208,7 +231,7 @@ export async function agentLoop<TResponse = unknown>(opts: AgentLoopOpts<TRespon
|
|
|
208
231
|
// `session/request_permission` path through CliAgentRunner.gateToolCall) can
|
|
209
232
|
// raise a human-approval `ctx.pause`, not just on the loop's own hooks.
|
|
210
233
|
...(opts.pause ? { pause: opts.pause } : {}),
|
|
211
|
-
...(opts.responseSchema ? { outputFormat: { type: "json_schema" as const, schema:
|
|
234
|
+
...(opts.responseSchema ? { outputFormat: { type: "json_schema" as const, schema: runtimeJsonSchema(opts.responseSchema) } } : {}),
|
|
212
235
|
});
|
|
213
236
|
|
|
214
237
|
// Heads-up when a caller registers tool-call gating on a runtime that
|
|
@@ -428,6 +451,11 @@ export async function agentLoop<TResponse = unknown>(opts: AgentLoopOpts<TRespon
|
|
|
428
451
|
signal: loopAbort.signal,
|
|
429
452
|
...(opts.inbox ? { inboxStream: opts.inbox } : {}),
|
|
430
453
|
})) {
|
|
454
|
+
// Live-only streaming chunk: the terminating `text` message carries
|
|
455
|
+
// the complete block, so the loop (accumulation, events, processors)
|
|
456
|
+
// ignores deltas — they exist for progressive-rendering consumers
|
|
457
|
+
// (the conversation cloud executor), not the workflow event stream.
|
|
458
|
+
if (rawMsg.type === "text_delta" || rawMsg.type === "usage_delta") continue;
|
|
431
459
|
// processOutput chain — deny drops the message from accumulation;
|
|
432
460
|
// abort ends the loop. Continue carries the (possibly mutated)
|
|
433
461
|
// message forward.
|
|
@@ -441,6 +469,7 @@ export async function agentLoop<TResponse = unknown>(opts: AgentLoopOpts<TRespon
|
|
|
441
469
|
continue;
|
|
442
470
|
}
|
|
443
471
|
const msg = outputVerdict.value;
|
|
472
|
+
if (msg.type === "text_delta" || msg.type === "usage_delta") continue; // a processor cannot re-introduce a live-only chunk
|
|
444
473
|
opts.onAgentEvent?.(iteration, msg);
|
|
445
474
|
// Usage summaries carry the resolved model so the server can price
|
|
446
475
|
// token rows per model without correlating back to agent.spawned.
|