@bastani/atomic 0.8.28-alpha.4 → 0.8.29-alpha.2
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/CHANGELOG.md +75 -0
- package/dist/builtin/cursor/CHANGELOG.md +27 -0
- package/dist/builtin/cursor/LICENSE +26 -0
- package/dist/builtin/cursor/README.md +22 -0
- package/dist/builtin/cursor/index.ts +9 -0
- package/dist/builtin/cursor/package.json +46 -0
- package/dist/builtin/cursor/src/auth.ts +352 -0
- package/dist/builtin/cursor/src/catalog-cache.ts +155 -0
- package/dist/builtin/cursor/src/config.ts +123 -0
- package/dist/builtin/cursor/src/conversation-state.ts +135 -0
- package/dist/builtin/cursor/src/cursor-models-raw.json +583 -0
- package/dist/builtin/cursor/src/model-mapper.ts +270 -0
- package/dist/builtin/cursor/src/models.ts +54 -0
- package/dist/builtin/cursor/src/native-loader.ts +71 -0
- package/dist/builtin/cursor/src/proto/README.md +34 -0
- package/dist/builtin/cursor/src/proto/agent_pb.ts +15294 -0
- package/dist/builtin/cursor/src/proto/protobuf-codec.ts +717 -0
- package/dist/builtin/cursor/src/provider.ts +301 -0
- package/dist/builtin/cursor/src/stream.ts +564 -0
- package/dist/builtin/cursor/src/transport.ts +791 -0
- package/dist/builtin/intercom/CHANGELOG.md +10 -0
- package/dist/builtin/intercom/package.json +2 -2
- package/dist/builtin/intercom/skills/intercom/SKILL.md +5 -5
- package/dist/builtin/mcp/CHANGELOG.md +10 -0
- package/dist/builtin/mcp/package.json +3 -3
- package/dist/builtin/subagents/CHANGELOG.md +18 -0
- package/dist/builtin/subagents/README.md +7 -3
- package/dist/builtin/subagents/agents/codebase-online-researcher.md +9 -24
- package/dist/builtin/subagents/agents/debugger.md +3 -5
- package/dist/builtin/subagents/package.json +4 -4
- package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +2 -1
- package/dist/builtin/subagents/src/runs/foreground/execution.ts +2 -1
- package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +1 -0
- package/dist/builtin/subagents/src/runs/shared/pi-args.ts +19 -2
- package/dist/builtin/subagents/src/runs/shared/structured-output.ts +271 -10
- package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +12 -39
- package/dist/builtin/subagents/src/shared/types.ts +1 -0
- package/dist/builtin/subagents/src/shared/utils.ts +50 -10
- package/dist/builtin/subagents/src/slash/saved-chain-mapping.ts +77 -0
- package/dist/builtin/subagents/src/slash/slash-commands.ts +1 -55
- package/dist/builtin/web-access/CHANGELOG.md +11 -1
- package/dist/builtin/web-access/README.md +1 -1
- package/dist/builtin/web-access/github-extract.ts +1 -1
- package/dist/builtin/web-access/package.json +3 -3
- package/dist/builtin/workflows/CHANGELOG.md +44 -0
- package/dist/builtin/workflows/README.md +19 -1
- package/dist/builtin/workflows/package.json +2 -2
- package/dist/builtin/workflows/skills/research-codebase/SKILL.md +17 -3
- package/dist/builtin/workflows/src/extension/wiring.ts +17 -1
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +34 -0
- package/dist/builtin/workflows/src/runs/foreground/executor.ts +13 -2
- package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +86 -14
- package/dist/builtin/workflows/src/shared/authoring-contract.d.ts +11 -3
- package/dist/builtin/workflows/src/shared/types.ts +8 -4
- package/dist/builtin/workflows/src/tui/overlay-adapter.ts +64 -2
- package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +8 -8
- package/dist/builtin/workflows/src/tui/workflow-status.ts +2 -0
- package/dist/core/builtin-packages.d.ts.map +1 -1
- package/dist/core/builtin-packages.js +6 -0
- package/dist/core/builtin-packages.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/types.d.ts +20 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/model-resolver.d.ts +1 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +17 -8
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/package-manager.d.ts +11 -9
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +55 -10
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/project-trust.d.ts +1 -0
- package/dist/core/project-trust.d.ts.map +1 -1
- package/dist/core/project-trust.js +3 -3
- package/dist/core/project-trust.js.map +1 -1
- package/dist/core/resource-loader.d.ts +9 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +72 -9
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +3 -3
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +5 -5
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +1 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/structured-output.d.ts +39 -0
- package/dist/core/tools/structured-output.d.ts.map +1 -0
- package/dist/core/tools/structured-output.js +141 -0
- package/dist/core/tools/structured-output.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +36 -14
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts +3 -0
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +16 -0
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +11 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +158 -11
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +39 -0
- package/dist/modes/print-mode.js.map +1 -1
- package/docs/custom-provider.md +1 -0
- package/docs/extensions.md +2 -2
- package/docs/models.md +2 -0
- package/docs/packages.md +3 -1
- package/docs/providers.md +15 -0
- package/docs/sdk.md +61 -0
- package/docs/security.md +1 -1
- package/docs/subagents.md +21 -0
- package/docs/usage.md +2 -0
- package/docs/workflows.md +10 -7
- package/examples/extensions/README.md +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/structured-output.ts +22 -53
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +12 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"print-mode.d.ts","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAoB,YAAY,
|
|
1
|
+
{"version":3,"file":"print-mode.d.ts","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAoB,YAAY,EAAqB,MAAM,uBAAuB,CAAC;AAE/F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAO5E;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,yEAAyE;IACzE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,wDAAwD;IACxD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;CAC/B;AAiED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,WAAW,EAAE,mBAAmB,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqK/G","sourcesContent":["/**\n * Print mode (single-shot): Send prompts, output result, exit.\n *\n * Used for:\n * - `pi -p \"prompt\"` - text output\n * - `pi --mode json \"prompt\"` - JSON event stream\n */\n\nimport type { AssistantMessage, ImageContent, ToolResultMessage } from \"@earendil-works/pi-ai\";\nimport type { AgentSessionEvent } from \"../core/agent-session.ts\";\nimport type { AgentSessionRuntime } from \"../core/agent-session-runtime.ts\";\nimport type { ExtensionError } from \"../core/extensions/index.ts\";\nimport type { ToolDefinition } from \"../core/extensions/types.ts\";\nimport type { CustomMessage } from \"../core/messages.ts\";\nimport { flushRawStdout, writeRawStdout } from \"../core/output-guard.ts\";\nimport { killTrackedDetachedChildren } from \"../utils/shell.ts\";\n\n/**\n * Options for print mode.\n */\nexport interface PrintModeOptions {\n\t/** Output mode: \"text\" for final response only, \"json\" for all events */\n\tmode: \"text\" | \"json\";\n\t/** Array of additional prompts to send after initialMessage */\n\tmessages?: string[];\n\t/** First message to send (may contain @file content) */\n\tinitialMessage?: string;\n\t/** Images to attach to the initial message */\n\tinitialImages?: ImageContent[];\n}\n\nfunction isCommandExtensionError(error: ExtensionError): boolean {\n\treturn error.event === \"command\" || error.extensionPath.startsWith(\"command:\");\n}\n\nfunction displayableCustomText(message: CustomMessage): string | undefined {\n\tif (message.display !== true) return undefined;\n\tif (typeof message.content === \"string\") return message.content;\n\n\tlet text = \"\";\n\tlet hasTextPart = false;\n\tfor (const part of message.content) {\n\t\tif (part.type === \"text\") {\n\t\t\thasTextPart = true;\n\t\t\ttext += part.text;\n\t\t}\n\t}\n\n\treturn hasTextPart ? text : undefined;\n}\n\ntype MaybeTerminatingToolResult = {\n\tterminate?: boolean;\n};\n\ntype ToolExecutionEndSessionEvent = Extract<AgentSessionEvent, { type: \"tool_execution_end\" }>;\n\ntype GetToolDefinition = (name: string) => ToolDefinition | undefined;\n\nfunction isStructuredOutputTool(getToolDefinition: GetToolDefinition, name: string): boolean {\n\treturn getToolDefinition(name)?.structuredOutput === true;\n}\n\nfunction isTerminatingStructuredOutputEvent(\n\tevent: AgentSessionEvent,\n\tgetToolDefinition: GetToolDefinition,\n): event is ToolExecutionEndSessionEvent {\n\tif (event.type !== \"tool_execution_end\") return false;\n\tif (!isStructuredOutputTool(getToolDefinition, event.toolName)) return false;\n\tif (event.isError) return false;\n\tconst result = event.result as MaybeTerminatingToolResult | undefined;\n\treturn result?.terminate === true;\n}\n\nfunction textFromToolResult(message: ToolResultMessage): string | undefined {\n\tlet text = \"\";\n\tlet hasTextPart = false;\n\tfor (const part of message.content) {\n\t\tif (part.type === \"text\") {\n\t\t\thasTextPart = true;\n\t\t\ttext += part.text;\n\t\t}\n\t}\n\treturn hasTextPart ? text : undefined;\n}\n\nfunction terminatingStructuredOutputText(\n\tmessage: ToolResultMessage,\n\tterminatingStructuredOutputCallIds: ReadonlySet<string>,\n): string | undefined {\n\tif (!terminatingStructuredOutputCallIds.has(message.toolCallId)) return undefined;\n\treturn textFromToolResult(message);\n}\n\n/**\n * Run in print (single-shot) mode.\n * Sends prompts to the agent and outputs the result.\n */\nexport async function runPrintMode(runtimeHost: AgentSessionRuntime, options: PrintModeOptions): Promise<number> {\n\tconst { mode, messages = [], initialMessage, initialImages } = options;\n\tlet exitCode = 0;\n\tlet suppressFinalOutput = false;\n\tlet activePromptHadCommandError = false;\n\tlet session = runtimeHost.session;\n\tlet unsubscribe: (() => void) | undefined;\n\tconst terminatingStructuredOutputCallIds = new Set<string>();\n\tlet disposed = false;\n\tconst signalCleanupHandlers: Array<() => void> = [];\n\n\tconst disposeRuntime = async (): Promise<void> => {\n\t\tif (disposed) return;\n\t\tdisposed = true;\n\t\tunsubscribe?.();\n\t\tawait runtimeHost.dispose();\n\t};\n\n\tconst registerSignalHandlers = (): void => {\n\t\tconst signals: NodeJS.Signals[] = [\"SIGTERM\"];\n\t\tif (process.platform !== \"win32\") {\n\t\t\tsignals.push(\"SIGHUP\");\n\t\t}\n\n\t\tfor (const signal of signals) {\n\t\t\tconst handler = () => {\n\t\t\t\tkillTrackedDetachedChildren();\n\t\t\t\tvoid disposeRuntime().finally(() => {\n\t\t\t\t\tprocess.exit(signal === \"SIGHUP\" ? 129 : 143);\n\t\t\t\t});\n\t\t\t};\n\t\t\tprocess.on(signal, handler);\n\t\t\tsignalCleanupHandlers.push(() => process.off(signal, handler));\n\t\t}\n\t};\n\n\tregisterSignalHandlers();\n\n\tconst promptWithScopedCommandSuppression = async (\n\t\ttext: string,\n\t\tpromptOptions?: { images?: ImageContent[] },\n\t): Promise<void> => {\n\t\tactivePromptHadCommandError = false;\n\t\ttry {\n\t\t\tif (promptOptions === undefined) {\n\t\t\t\tawait session.prompt(text);\n\t\t\t} else {\n\t\t\t\tawait session.prompt(text, promptOptions);\n\t\t\t}\n\t\t} finally {\n\t\t\t// Final-output suppression is scoped to the most recent prompt so a\n\t\t\t// later successful prompt in the same invocation can still print its\n\t\t\t// result. The non-zero exit code remains sticky across prompts.\n\t\t\tsuppressFinalOutput = activePromptHadCommandError;\n\t\t}\n\t};\n\n\tconst rebindSession = async (): Promise<void> => {\n\t\tsession = runtimeHost.session;\n\t\tawait session.bindExtensions({\n\t\t\tmode: mode === \"json\" ? \"json\" : \"print\",\n\t\t\tcommandContextActions: {\n\t\t\t\twaitForIdle: () => session.agent.waitForIdle(),\n\t\t\t\tnewSession: async (newSessionOptions) => runtimeHost.newSession(newSessionOptions),\n\t\t\t\tfork: async (entryId, forkOptions) => {\n\t\t\t\t\tconst result = await runtimeHost.fork(entryId, forkOptions);\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tnavigateTree: async (targetId, navigateOptions) => {\n\t\t\t\t\tconst result = await session.navigateTree(targetId, {\n\t\t\t\t\t\tsummarize: navigateOptions?.summarize,\n\t\t\t\t\t\tcustomInstructions: navigateOptions?.customInstructions,\n\t\t\t\t\t\treplaceInstructions: navigateOptions?.replaceInstructions,\n\t\t\t\t\t\tlabel: navigateOptions?.label,\n\t\t\t\t\t});\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tswitchSession: async (sessionPath, switchOptions) => {\n\t\t\t\t\treturn runtimeHost.switchSession(sessionPath, switchOptions);\n\t\t\t\t},\n\t\t\t\treload: async () => {\n\t\t\t\t\tawait session.reload();\n\t\t\t\t},\n\t\t\t},\n\t\t\tonError: (err) => {\n\t\t\t\tconst isCommandError = isCommandExtensionError(err);\n\t\t\t\tif (isCommandError) exitCode = 1;\n\t\t\t\tactivePromptHadCommandError = activePromptHadCommandError || isCommandError;\n\t\t\t\tconsole.error(`Extension error (${err.extensionPath}): ${err.error}`);\n\t\t\t},\n\t\t});\n\n\t\tunsubscribe?.();\n\t\tunsubscribe = session.subscribe((event) => {\n\t\t\tif (isTerminatingStructuredOutputEvent(event, (name) => session.getToolDefinition(name))) {\n\t\t\t\tterminatingStructuredOutputCallIds.add(event.toolCallId);\n\t\t\t}\n\t\t\tif (mode === \"json\") {\n\t\t\t\twriteRawStdout(`${JSON.stringify(event)}\\n`);\n\t\t\t}\n\t\t});\n\t};\n\n\truntimeHost.setRebindSession(async () => {\n\t\tawait rebindSession();\n\t});\n\n\ttry {\n\t\tif (mode === \"json\") {\n\t\t\tconst header = session.sessionManager.getHeader();\n\t\t\tif (header) {\n\t\t\t\twriteRawStdout(`${JSON.stringify(header)}\\n`);\n\t\t\t}\n\t\t}\n\n\t\tawait rebindSession();\n\n\t\tif (initialMessage) {\n\t\t\tawait promptWithScopedCommandSuppression(initialMessage, { images: initialImages });\n\t\t}\n\n\t\tfor (const message of messages) {\n\t\t\tawait promptWithScopedCommandSuppression(message);\n\t\t}\n\n\t\tif (mode === \"text\" && !suppressFinalOutput) {\n\t\t\tconst state = session.state;\n\t\t\tconst lastMessage = state.messages[state.messages.length - 1];\n\n\t\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = lastMessage as AssistantMessage;\n\t\t\t\tif (assistantMsg.stopReason === \"error\" || assistantMsg.stopReason === \"aborted\") {\n\t\t\t\t\tconsole.error(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);\n\t\t\t\t\texitCode = 1;\n\t\t\t\t} else {\n\t\t\t\t\tfor (const content of assistantMsg.content) {\n\t\t\t\t\t\tif (content.type === \"text\") {\n\t\t\t\t\t\t\twriteRawStdout(`${content.text}\\n`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (lastMessage?.role === \"custom\") {\n\t\t\t\tconst text = displayableCustomText(lastMessage as CustomMessage);\n\t\t\t\tif (text !== undefined) {\n\t\t\t\t\twriteRawStdout(`${text}\\n`);\n\t\t\t\t}\n\t\t\t} else if (lastMessage?.role === \"toolResult\") {\n\t\t\t\tconst text = terminatingStructuredOutputText(lastMessage as ToolResultMessage, terminatingStructuredOutputCallIds);\n\t\t\t\tif (text !== undefined) {\n\t\t\t\t\twriteRawStdout(`${text}\\n`);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn exitCode;\n\t} catch (error: unknown) {\n\t\tconsole.error(error instanceof Error ? error.message : String(error));\n\t\treturn 1;\n\t} finally {\n\t\tfor (const cleanup of signalCleanupHandlers) {\n\t\t\tcleanup();\n\t\t}\n\t\tawait disposeRuntime();\n\t\tawait flushRawStdout();\n\t}\n}\n"]}
|
package/dist/modes/print-mode.js
CHANGED
|
@@ -25,6 +25,35 @@ function displayableCustomText(message) {
|
|
|
25
25
|
}
|
|
26
26
|
return hasTextPart ? text : undefined;
|
|
27
27
|
}
|
|
28
|
+
function isStructuredOutputTool(getToolDefinition, name) {
|
|
29
|
+
return getToolDefinition(name)?.structuredOutput === true;
|
|
30
|
+
}
|
|
31
|
+
function isTerminatingStructuredOutputEvent(event, getToolDefinition) {
|
|
32
|
+
if (event.type !== "tool_execution_end")
|
|
33
|
+
return false;
|
|
34
|
+
if (!isStructuredOutputTool(getToolDefinition, event.toolName))
|
|
35
|
+
return false;
|
|
36
|
+
if (event.isError)
|
|
37
|
+
return false;
|
|
38
|
+
const result = event.result;
|
|
39
|
+
return result?.terminate === true;
|
|
40
|
+
}
|
|
41
|
+
function textFromToolResult(message) {
|
|
42
|
+
let text = "";
|
|
43
|
+
let hasTextPart = false;
|
|
44
|
+
for (const part of message.content) {
|
|
45
|
+
if (part.type === "text") {
|
|
46
|
+
hasTextPart = true;
|
|
47
|
+
text += part.text;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return hasTextPart ? text : undefined;
|
|
51
|
+
}
|
|
52
|
+
function terminatingStructuredOutputText(message, terminatingStructuredOutputCallIds) {
|
|
53
|
+
if (!terminatingStructuredOutputCallIds.has(message.toolCallId))
|
|
54
|
+
return undefined;
|
|
55
|
+
return textFromToolResult(message);
|
|
56
|
+
}
|
|
28
57
|
/**
|
|
29
58
|
* Run in print (single-shot) mode.
|
|
30
59
|
* Sends prompts to the agent and outputs the result.
|
|
@@ -36,6 +65,7 @@ export async function runPrintMode(runtimeHost, options) {
|
|
|
36
65
|
let activePromptHadCommandError = false;
|
|
37
66
|
let session = runtimeHost.session;
|
|
38
67
|
let unsubscribe;
|
|
68
|
+
const terminatingStructuredOutputCallIds = new Set();
|
|
39
69
|
let disposed = false;
|
|
40
70
|
const signalCleanupHandlers = [];
|
|
41
71
|
const disposeRuntime = async () => {
|
|
@@ -116,6 +146,9 @@ export async function runPrintMode(runtimeHost, options) {
|
|
|
116
146
|
});
|
|
117
147
|
unsubscribe?.();
|
|
118
148
|
unsubscribe = session.subscribe((event) => {
|
|
149
|
+
if (isTerminatingStructuredOutputEvent(event, (name) => session.getToolDefinition(name))) {
|
|
150
|
+
terminatingStructuredOutputCallIds.add(event.toolCallId);
|
|
151
|
+
}
|
|
119
152
|
if (mode === "json") {
|
|
120
153
|
writeRawStdout(`${JSON.stringify(event)}\n`);
|
|
121
154
|
}
|
|
@@ -161,6 +194,12 @@ export async function runPrintMode(runtimeHost, options) {
|
|
|
161
194
|
writeRawStdout(`${text}\n`);
|
|
162
195
|
}
|
|
163
196
|
}
|
|
197
|
+
else if (lastMessage?.role === "toolResult") {
|
|
198
|
+
const text = terminatingStructuredOutputText(lastMessage, terminatingStructuredOutputCallIds);
|
|
199
|
+
if (text !== undefined) {
|
|
200
|
+
writeRawStdout(`${text}\n`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
164
203
|
}
|
|
165
204
|
return exitCode;
|
|
166
205
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"print-mode.js","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Print mode (single-shot): Send prompts, output result, exit.\n *\n * Used for:\n * - `pi -p \"prompt\"` - text output\n * - `pi --mode json \"prompt\"` - JSON event stream\n */\n\nimport type { AssistantMessage, ImageContent } from \"@earendil-works/pi-ai\";\nimport type { AgentSessionRuntime } from \"../core/agent-session-runtime.ts\";\nimport type { ExtensionError } from \"../core/extensions/index.ts\";\nimport type { CustomMessage } from \"../core/messages.ts\";\nimport { flushRawStdout, writeRawStdout } from \"../core/output-guard.ts\";\nimport { killTrackedDetachedChildren } from \"../utils/shell.ts\";\n\n/**\n * Options for print mode.\n */\nexport interface PrintModeOptions {\n\t/** Output mode: \"text\" for final response only, \"json\" for all events */\n\tmode: \"text\" | \"json\";\n\t/** Array of additional prompts to send after initialMessage */\n\tmessages?: string[];\n\t/** First message to send (may contain @file content) */\n\tinitialMessage?: string;\n\t/** Images to attach to the initial message */\n\tinitialImages?: ImageContent[];\n}\n\nfunction isCommandExtensionError(error: ExtensionError): boolean {\n\treturn error.event === \"command\" || error.extensionPath.startsWith(\"command:\");\n}\n\nfunction displayableCustomText(message: CustomMessage): string | undefined {\n\tif (message.display !== true) return undefined;\n\tif (typeof message.content === \"string\") return message.content;\n\n\tlet text = \"\";\n\tlet hasTextPart = false;\n\tfor (const part of message.content) {\n\t\tif (part.type === \"text\") {\n\t\t\thasTextPart = true;\n\t\t\ttext += part.text;\n\t\t}\n\t}\n\n\treturn hasTextPart ? text : undefined;\n}\n\n/**\n * Run in print (single-shot) mode.\n * Sends prompts to the agent and outputs the result.\n */\nexport async function runPrintMode(runtimeHost: AgentSessionRuntime, options: PrintModeOptions): Promise<number> {\n\tconst { mode, messages = [], initialMessage, initialImages } = options;\n\tlet exitCode = 0;\n\tlet suppressFinalOutput = false;\n\tlet activePromptHadCommandError = false;\n\tlet session = runtimeHost.session;\n\tlet unsubscribe: (() => void) | undefined;\n\tlet disposed = false;\n\tconst signalCleanupHandlers: Array<() => void> = [];\n\n\tconst disposeRuntime = async (): Promise<void> => {\n\t\tif (disposed) return;\n\t\tdisposed = true;\n\t\tunsubscribe?.();\n\t\tawait runtimeHost.dispose();\n\t};\n\n\tconst registerSignalHandlers = (): void => {\n\t\tconst signals: NodeJS.Signals[] = [\"SIGTERM\"];\n\t\tif (process.platform !== \"win32\") {\n\t\t\tsignals.push(\"SIGHUP\");\n\t\t}\n\n\t\tfor (const signal of signals) {\n\t\t\tconst handler = () => {\n\t\t\t\tkillTrackedDetachedChildren();\n\t\t\t\tvoid disposeRuntime().finally(() => {\n\t\t\t\t\tprocess.exit(signal === \"SIGHUP\" ? 129 : 143);\n\t\t\t\t});\n\t\t\t};\n\t\t\tprocess.on(signal, handler);\n\t\t\tsignalCleanupHandlers.push(() => process.off(signal, handler));\n\t\t}\n\t};\n\n\tregisterSignalHandlers();\n\n\tconst promptWithScopedCommandSuppression = async (\n\t\ttext: string,\n\t\tpromptOptions?: { images?: ImageContent[] },\n\t): Promise<void> => {\n\t\tactivePromptHadCommandError = false;\n\t\ttry {\n\t\t\tif (promptOptions === undefined) {\n\t\t\t\tawait session.prompt(text);\n\t\t\t} else {\n\t\t\t\tawait session.prompt(text, promptOptions);\n\t\t\t}\n\t\t} finally {\n\t\t\t// Final-output suppression is scoped to the most recent prompt so a\n\t\t\t// later successful prompt in the same invocation can still print its\n\t\t\t// result. The non-zero exit code remains sticky across prompts.\n\t\t\tsuppressFinalOutput = activePromptHadCommandError;\n\t\t}\n\t};\n\n\tconst rebindSession = async (): Promise<void> => {\n\t\tsession = runtimeHost.session;\n\t\tawait session.bindExtensions({\n\t\t\tmode: mode === \"json\" ? \"json\" : \"print\",\n\t\t\tcommandContextActions: {\n\t\t\t\twaitForIdle: () => session.agent.waitForIdle(),\n\t\t\t\tnewSession: async (newSessionOptions) => runtimeHost.newSession(newSessionOptions),\n\t\t\t\tfork: async (entryId, forkOptions) => {\n\t\t\t\t\tconst result = await runtimeHost.fork(entryId, forkOptions);\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tnavigateTree: async (targetId, navigateOptions) => {\n\t\t\t\t\tconst result = await session.navigateTree(targetId, {\n\t\t\t\t\t\tsummarize: navigateOptions?.summarize,\n\t\t\t\t\t\tcustomInstructions: navigateOptions?.customInstructions,\n\t\t\t\t\t\treplaceInstructions: navigateOptions?.replaceInstructions,\n\t\t\t\t\t\tlabel: navigateOptions?.label,\n\t\t\t\t\t});\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tswitchSession: async (sessionPath, switchOptions) => {\n\t\t\t\t\treturn runtimeHost.switchSession(sessionPath, switchOptions);\n\t\t\t\t},\n\t\t\t\treload: async () => {\n\t\t\t\t\tawait session.reload();\n\t\t\t\t},\n\t\t\t},\n\t\t\tonError: (err) => {\n\t\t\t\tconst isCommandError = isCommandExtensionError(err);\n\t\t\t\tif (isCommandError) exitCode = 1;\n\t\t\t\tactivePromptHadCommandError = activePromptHadCommandError || isCommandError;\n\t\t\t\tconsole.error(`Extension error (${err.extensionPath}): ${err.error}`);\n\t\t\t},\n\t\t});\n\n\t\tunsubscribe?.();\n\t\tunsubscribe = session.subscribe((event) => {\n\t\t\tif (mode === \"json\") {\n\t\t\t\twriteRawStdout(`${JSON.stringify(event)}\\n`);\n\t\t\t}\n\t\t});\n\t};\n\n\truntimeHost.setRebindSession(async () => {\n\t\tawait rebindSession();\n\t});\n\n\ttry {\n\t\tif (mode === \"json\") {\n\t\t\tconst header = session.sessionManager.getHeader();\n\t\t\tif (header) {\n\t\t\t\twriteRawStdout(`${JSON.stringify(header)}\\n`);\n\t\t\t}\n\t\t}\n\n\t\tawait rebindSession();\n\n\t\tif (initialMessage) {\n\t\t\tawait promptWithScopedCommandSuppression(initialMessage, { images: initialImages });\n\t\t}\n\n\t\tfor (const message of messages) {\n\t\t\tawait promptWithScopedCommandSuppression(message);\n\t\t}\n\n\t\tif (mode === \"text\" && !suppressFinalOutput) {\n\t\t\tconst state = session.state;\n\t\t\tconst lastMessage = state.messages[state.messages.length - 1];\n\n\t\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = lastMessage as AssistantMessage;\n\t\t\t\tif (assistantMsg.stopReason === \"error\" || assistantMsg.stopReason === \"aborted\") {\n\t\t\t\t\tconsole.error(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);\n\t\t\t\t\texitCode = 1;\n\t\t\t\t} else {\n\t\t\t\t\tfor (const content of assistantMsg.content) {\n\t\t\t\t\t\tif (content.type === \"text\") {\n\t\t\t\t\t\t\twriteRawStdout(`${content.text}\\n`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (lastMessage?.role === \"custom\") {\n\t\t\t\tconst text = displayableCustomText(lastMessage as CustomMessage);\n\t\t\t\tif (text !== undefined) {\n\t\t\t\t\twriteRawStdout(`${text}\\n`);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn exitCode;\n\t} catch (error: unknown) {\n\t\tconsole.error(error instanceof Error ? error.message : String(error));\n\t\treturn 1;\n\t} finally {\n\t\tfor (const cleanup of signalCleanupHandlers) {\n\t\t\tcleanup();\n\t\t}\n\t\tawait disposeRuntime();\n\t\tawait flushRawStdout();\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"print-mode.js","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Print mode (single-shot): Send prompts, output result, exit.\n *\n * Used for:\n * - `pi -p \"prompt\"` - text output\n * - `pi --mode json \"prompt\"` - JSON event stream\n */\n\nimport type { AssistantMessage, ImageContent, ToolResultMessage } from \"@earendil-works/pi-ai\";\nimport type { AgentSessionEvent } from \"../core/agent-session.ts\";\nimport type { AgentSessionRuntime } from \"../core/agent-session-runtime.ts\";\nimport type { ExtensionError } from \"../core/extensions/index.ts\";\nimport type { ToolDefinition } from \"../core/extensions/types.ts\";\nimport type { CustomMessage } from \"../core/messages.ts\";\nimport { flushRawStdout, writeRawStdout } from \"../core/output-guard.ts\";\nimport { killTrackedDetachedChildren } from \"../utils/shell.ts\";\n\n/**\n * Options for print mode.\n */\nexport interface PrintModeOptions {\n\t/** Output mode: \"text\" for final response only, \"json\" for all events */\n\tmode: \"text\" | \"json\";\n\t/** Array of additional prompts to send after initialMessage */\n\tmessages?: string[];\n\t/** First message to send (may contain @file content) */\n\tinitialMessage?: string;\n\t/** Images to attach to the initial message */\n\tinitialImages?: ImageContent[];\n}\n\nfunction isCommandExtensionError(error: ExtensionError): boolean {\n\treturn error.event === \"command\" || error.extensionPath.startsWith(\"command:\");\n}\n\nfunction displayableCustomText(message: CustomMessage): string | undefined {\n\tif (message.display !== true) return undefined;\n\tif (typeof message.content === \"string\") return message.content;\n\n\tlet text = \"\";\n\tlet hasTextPart = false;\n\tfor (const part of message.content) {\n\t\tif (part.type === \"text\") {\n\t\t\thasTextPart = true;\n\t\t\ttext += part.text;\n\t\t}\n\t}\n\n\treturn hasTextPart ? text : undefined;\n}\n\ntype MaybeTerminatingToolResult = {\n\tterminate?: boolean;\n};\n\ntype ToolExecutionEndSessionEvent = Extract<AgentSessionEvent, { type: \"tool_execution_end\" }>;\n\ntype GetToolDefinition = (name: string) => ToolDefinition | undefined;\n\nfunction isStructuredOutputTool(getToolDefinition: GetToolDefinition, name: string): boolean {\n\treturn getToolDefinition(name)?.structuredOutput === true;\n}\n\nfunction isTerminatingStructuredOutputEvent(\n\tevent: AgentSessionEvent,\n\tgetToolDefinition: GetToolDefinition,\n): event is ToolExecutionEndSessionEvent {\n\tif (event.type !== \"tool_execution_end\") return false;\n\tif (!isStructuredOutputTool(getToolDefinition, event.toolName)) return false;\n\tif (event.isError) return false;\n\tconst result = event.result as MaybeTerminatingToolResult | undefined;\n\treturn result?.terminate === true;\n}\n\nfunction textFromToolResult(message: ToolResultMessage): string | undefined {\n\tlet text = \"\";\n\tlet hasTextPart = false;\n\tfor (const part of message.content) {\n\t\tif (part.type === \"text\") {\n\t\t\thasTextPart = true;\n\t\t\ttext += part.text;\n\t\t}\n\t}\n\treturn hasTextPart ? text : undefined;\n}\n\nfunction terminatingStructuredOutputText(\n\tmessage: ToolResultMessage,\n\tterminatingStructuredOutputCallIds: ReadonlySet<string>,\n): string | undefined {\n\tif (!terminatingStructuredOutputCallIds.has(message.toolCallId)) return undefined;\n\treturn textFromToolResult(message);\n}\n\n/**\n * Run in print (single-shot) mode.\n * Sends prompts to the agent and outputs the result.\n */\nexport async function runPrintMode(runtimeHost: AgentSessionRuntime, options: PrintModeOptions): Promise<number> {\n\tconst { mode, messages = [], initialMessage, initialImages } = options;\n\tlet exitCode = 0;\n\tlet suppressFinalOutput = false;\n\tlet activePromptHadCommandError = false;\n\tlet session = runtimeHost.session;\n\tlet unsubscribe: (() => void) | undefined;\n\tconst terminatingStructuredOutputCallIds = new Set<string>();\n\tlet disposed = false;\n\tconst signalCleanupHandlers: Array<() => void> = [];\n\n\tconst disposeRuntime = async (): Promise<void> => {\n\t\tif (disposed) return;\n\t\tdisposed = true;\n\t\tunsubscribe?.();\n\t\tawait runtimeHost.dispose();\n\t};\n\n\tconst registerSignalHandlers = (): void => {\n\t\tconst signals: NodeJS.Signals[] = [\"SIGTERM\"];\n\t\tif (process.platform !== \"win32\") {\n\t\t\tsignals.push(\"SIGHUP\");\n\t\t}\n\n\t\tfor (const signal of signals) {\n\t\t\tconst handler = () => {\n\t\t\t\tkillTrackedDetachedChildren();\n\t\t\t\tvoid disposeRuntime().finally(() => {\n\t\t\t\t\tprocess.exit(signal === \"SIGHUP\" ? 129 : 143);\n\t\t\t\t});\n\t\t\t};\n\t\t\tprocess.on(signal, handler);\n\t\t\tsignalCleanupHandlers.push(() => process.off(signal, handler));\n\t\t}\n\t};\n\n\tregisterSignalHandlers();\n\n\tconst promptWithScopedCommandSuppression = async (\n\t\ttext: string,\n\t\tpromptOptions?: { images?: ImageContent[] },\n\t): Promise<void> => {\n\t\tactivePromptHadCommandError = false;\n\t\ttry {\n\t\t\tif (promptOptions === undefined) {\n\t\t\t\tawait session.prompt(text);\n\t\t\t} else {\n\t\t\t\tawait session.prompt(text, promptOptions);\n\t\t\t}\n\t\t} finally {\n\t\t\t// Final-output suppression is scoped to the most recent prompt so a\n\t\t\t// later successful prompt in the same invocation can still print its\n\t\t\t// result. The non-zero exit code remains sticky across prompts.\n\t\t\tsuppressFinalOutput = activePromptHadCommandError;\n\t\t}\n\t};\n\n\tconst rebindSession = async (): Promise<void> => {\n\t\tsession = runtimeHost.session;\n\t\tawait session.bindExtensions({\n\t\t\tmode: mode === \"json\" ? \"json\" : \"print\",\n\t\t\tcommandContextActions: {\n\t\t\t\twaitForIdle: () => session.agent.waitForIdle(),\n\t\t\t\tnewSession: async (newSessionOptions) => runtimeHost.newSession(newSessionOptions),\n\t\t\t\tfork: async (entryId, forkOptions) => {\n\t\t\t\t\tconst result = await runtimeHost.fork(entryId, forkOptions);\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tnavigateTree: async (targetId, navigateOptions) => {\n\t\t\t\t\tconst result = await session.navigateTree(targetId, {\n\t\t\t\t\t\tsummarize: navigateOptions?.summarize,\n\t\t\t\t\t\tcustomInstructions: navigateOptions?.customInstructions,\n\t\t\t\t\t\treplaceInstructions: navigateOptions?.replaceInstructions,\n\t\t\t\t\t\tlabel: navigateOptions?.label,\n\t\t\t\t\t});\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tswitchSession: async (sessionPath, switchOptions) => {\n\t\t\t\t\treturn runtimeHost.switchSession(sessionPath, switchOptions);\n\t\t\t\t},\n\t\t\t\treload: async () => {\n\t\t\t\t\tawait session.reload();\n\t\t\t\t},\n\t\t\t},\n\t\t\tonError: (err) => {\n\t\t\t\tconst isCommandError = isCommandExtensionError(err);\n\t\t\t\tif (isCommandError) exitCode = 1;\n\t\t\t\tactivePromptHadCommandError = activePromptHadCommandError || isCommandError;\n\t\t\t\tconsole.error(`Extension error (${err.extensionPath}): ${err.error}`);\n\t\t\t},\n\t\t});\n\n\t\tunsubscribe?.();\n\t\tunsubscribe = session.subscribe((event) => {\n\t\t\tif (isTerminatingStructuredOutputEvent(event, (name) => session.getToolDefinition(name))) {\n\t\t\t\tterminatingStructuredOutputCallIds.add(event.toolCallId);\n\t\t\t}\n\t\t\tif (mode === \"json\") {\n\t\t\t\twriteRawStdout(`${JSON.stringify(event)}\\n`);\n\t\t\t}\n\t\t});\n\t};\n\n\truntimeHost.setRebindSession(async () => {\n\t\tawait rebindSession();\n\t});\n\n\ttry {\n\t\tif (mode === \"json\") {\n\t\t\tconst header = session.sessionManager.getHeader();\n\t\t\tif (header) {\n\t\t\t\twriteRawStdout(`${JSON.stringify(header)}\\n`);\n\t\t\t}\n\t\t}\n\n\t\tawait rebindSession();\n\n\t\tif (initialMessage) {\n\t\t\tawait promptWithScopedCommandSuppression(initialMessage, { images: initialImages });\n\t\t}\n\n\t\tfor (const message of messages) {\n\t\t\tawait promptWithScopedCommandSuppression(message);\n\t\t}\n\n\t\tif (mode === \"text\" && !suppressFinalOutput) {\n\t\t\tconst state = session.state;\n\t\t\tconst lastMessage = state.messages[state.messages.length - 1];\n\n\t\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = lastMessage as AssistantMessage;\n\t\t\t\tif (assistantMsg.stopReason === \"error\" || assistantMsg.stopReason === \"aborted\") {\n\t\t\t\t\tconsole.error(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);\n\t\t\t\t\texitCode = 1;\n\t\t\t\t} else {\n\t\t\t\t\tfor (const content of assistantMsg.content) {\n\t\t\t\t\t\tif (content.type === \"text\") {\n\t\t\t\t\t\t\twriteRawStdout(`${content.text}\\n`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (lastMessage?.role === \"custom\") {\n\t\t\t\tconst text = displayableCustomText(lastMessage as CustomMessage);\n\t\t\t\tif (text !== undefined) {\n\t\t\t\t\twriteRawStdout(`${text}\\n`);\n\t\t\t\t}\n\t\t\t} else if (lastMessage?.role === \"toolResult\") {\n\t\t\t\tconst text = terminatingStructuredOutputText(lastMessage as ToolResultMessage, terminatingStructuredOutputCallIds);\n\t\t\t\tif (text !== undefined) {\n\t\t\t\t\twriteRawStdout(`${text}\\n`);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn exitCode;\n\t} catch (error: unknown) {\n\t\tconsole.error(error instanceof Error ? error.message : String(error));\n\t\treturn 1;\n\t} finally {\n\t\tfor (const cleanup of signalCleanupHandlers) {\n\t\t\tcleanup();\n\t\t}\n\t\tawait disposeRuntime();\n\t\tawait flushRawStdout();\n\t}\n}\n"]}
|
package/docs/custom-provider.md
CHANGED
|
@@ -13,6 +13,7 @@ See these complete provider examples:
|
|
|
13
13
|
|
|
14
14
|
- [`examples/extensions/custom-provider-anthropic/`](https://github.com/bastani-inc/atomic/tree/main/packages/coding-agent/examples/extensions/custom-provider-anthropic)
|
|
15
15
|
- [`examples/extensions/custom-provider-gitlab-duo/`](https://github.com/bastani-inc/atomic/tree/main/packages/coding-agent/examples/extensions/custom-provider-gitlab-duo)
|
|
16
|
+
- Built-in reference: `packages/cursor/` registers the experimental first-party Cursor provider with OAuth, model mapping, and a native `streamSimple` adapter without a local proxy.
|
|
16
17
|
|
|
17
18
|
## Table of Contents
|
|
18
19
|
|
package/docs/extensions.md
CHANGED
|
@@ -1817,7 +1817,7 @@ pi.registerTool({
|
|
|
1817
1817
|
|
|
1818
1818
|
**Signaling errors:** To mark a tool execution as failed (sets `isError: true` on the result and reports it to the LLM), throw an error from `execute`. Returning a value never sets the error flag regardless of what properties you include in the return object.
|
|
1819
1819
|
|
|
1820
|
-
**Early termination:** Return `terminate: true` from `execute()` to hint that the automatic follow-up LLM call should be skipped after the current tool batch. This only takes effect when every finalized tool result in that batch is terminating.
|
|
1820
|
+
**Early termination:** Return `terminate: true` from `execute()` to hint that the automatic follow-up LLM call should be skipped after the current tool batch. This only takes effect when every finalized tool result in that batch is terminating. Atomic does not register `structured_output` in normal agent sessions by default; use `createStructuredOutputTool({ schema, capture, output, name })` when an extension, SDK session, workflow stage, or subagent runtime needs a schema-backed final-answer tool. The factory requires a top-level object tool-argument schema, uses that schema as the tool parameters directly, captures the flat tool arguments, and terminates the turn without a `{ value: ... }` wrapper. Wrap array or primitive final values in explicit object fields such as `{ items: [...] }` or `{ value: ... }`; in text print mode, a terminating result from a factory-created structured-output tool is emitted to stdout as the final response, including custom names such as `final_decision`. Hand-rolled terminating tools are not treated as printable structured-output results unless they opt into the same tool-definition metadata. Large final JSON stays inline for this tool instead of being redirected to `<persisted-output>`, preserving the machine-readable final-answer contract. Custom factory names are opt-in tools: if you register `final_decision`, include `final_decision` in any explicit `tools` allowlist; if you register the default `structured_output` name, it is available only to that session/runtime.
|
|
1821
1821
|
|
|
1822
1822
|
```typescript
|
|
1823
1823
|
// Correct: throw to signal an error
|
|
@@ -2592,7 +2592,7 @@ All examples in [examples/extensions/](https://github.com/bastani-inc/atomic/tre
|
|
|
2592
2592
|
| `questionnaire.ts` | Multi-step wizard tool | `registerTool`, `ui.custom` |
|
|
2593
2593
|
| `todo.ts` | Stateful tool with persistence | `registerTool`, `appendEntry`, `renderResult`, session events |
|
|
2594
2594
|
| `dynamic-tools.ts` | Register tools after startup and during commands | `registerTool`, `session_start`, `registerCommand` |
|
|
2595
|
-
| `structured-output.ts` |
|
|
2595
|
+
| `structured-output.ts` | Opt-in schema-specific `structured_output` tool using the canonical factory | `createStructuredOutputTool`, `registerTool`, terminating tool results |
|
|
2596
2596
|
| `truncated-tool.ts` | Output truncation example | `registerTool`, `truncateHead` |
|
|
2597
2597
|
| `tool-override.ts` | Override built-in read tool | `registerTool` (same name as built-in) |
|
|
2598
2598
|
| **Commands** |||
|
package/docs/models.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Add custom providers and models (Ollama, vLLM, LM Studio, proxies) via `~/.atomic/agent/models.json` (legacy `~/.pi/agent/models.json` is also read).
|
|
4
4
|
|
|
5
|
+
Built-in subscription providers such as Cursor (experimental) are selected with the same `provider/model` syntax, for example `cursor/composer-2`. Cursor is text-only in the initial experimental implementation; live private-API model metadata may fall back to estimated labels. Because Cursor support targets undocumented private endpoints with Cursor CLI-compatible headers, maintainers and users should explicitly accept the risk that it may conflict with Cursor's terms, break without notice, or affect the Cursor account used to authenticate.
|
|
6
|
+
|
|
5
7
|
## Table of Contents
|
|
6
8
|
|
|
7
9
|
- [Minimal Example](#minimal-example)
|
package/docs/packages.md
CHANGED
|
@@ -52,6 +52,8 @@ atomic -e npm:@foo/bar
|
|
|
52
52
|
atomic -e git:github.com/user/repo
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
For local directories, `-e <dir>` also borrows project-local Atomic resources under `<dir>/.atomic`, legacy `<dir>/.pi`, and `<dir>/.agents/skills` when present. Because borrowed extensions and workflows can execute code, Atomic resolves trust for that extension source before loading those borrowed project-local resources.
|
|
56
|
+
|
|
55
57
|
## Package Sources
|
|
56
58
|
|
|
57
59
|
Atomic accepts three source types in settings and `atomic install`.
|
|
@@ -113,7 +115,7 @@ atomic install git:git@github.com:user/repo@v1.0.0
|
|
|
113
115
|
./relative/path/to/package
|
|
114
116
|
```
|
|
115
117
|
|
|
116
|
-
Local paths point to files or directories on disk and are added to settings without copying. Relative paths are resolved against the settings file they appear in. If the path is a file, it loads as a single extension. If it is a directory, Atomic loads resources using package rules.
|
|
118
|
+
Local paths point to files or directories on disk and are added to settings without copying. Relative paths are resolved against the settings file they appear in. If the path is a file, it loads as a single extension. If it is a directory, Atomic loads resources using package rules. Temporary local directories supplied with `-e` may also expose `.atomic`/`.pi` project-local resources and `.agents/skills` after the extension source is trusted.
|
|
117
119
|
|
|
118
120
|
## Creating an Atomic Package
|
|
119
121
|
|
package/docs/providers.md
CHANGED
|
@@ -18,6 +18,7 @@ Use `/login` in interactive mode, then select a provider:
|
|
|
18
18
|
- ChatGPT Plus/Pro (Codex)
|
|
19
19
|
- Claude Pro/Max
|
|
20
20
|
- GitHub Copilot
|
|
21
|
+
- Cursor (experimental)
|
|
21
22
|
|
|
22
23
|
Use `/logout` to clear credentials. Tokens are stored in `~/.atomic/agent/auth.json` and auto-refresh when expired.
|
|
23
24
|
|
|
@@ -39,6 +40,20 @@ Anthropic subscription auth is active for Claude Pro/Max accounts. Third-party h
|
|
|
39
40
|
- Press Enter for github.com, or enter your GitHub Enterprise Server domain
|
|
40
41
|
- If you get "model not supported", enable it in VS Code: Copilot Chat → model selector → select model → "Enable"
|
|
41
42
|
|
|
43
|
+
### Cursor (experimental)
|
|
44
|
+
|
|
45
|
+
Cursor support is bundled as the first-party `@bastani/cursor` extension and appears in `/login` as **Cursor (experimental)**. It uses Cursor's browser PKCE flow and stores OAuth credentials in `~/.atomic/agent/auth.json`; do not paste Cursor tokens into environment variables, command-line arguments, or custom proxies. Atomic identifies as a Cursor CLI-compatible client against private endpoints; maintainers and users should explicitly accept that this may conflict with Cursor's terms of service, stop working without notice, or affect the Cursor account used to authenticate.
|
|
46
|
+
|
|
47
|
+
Current limitations:
|
|
48
|
+
|
|
49
|
+
- Cursor uses private, undocumented APIs and Cursor CLI-compatible headers. Atomic keeps the transport isolated and labels this provider experimental because Cursor may change the protocol without notice; use may conflict with Cursor's terms of service or provider-side account policies.
|
|
50
|
+
- Text input is supported; vision/image input is rejected with a clear error.
|
|
51
|
+
- Model metadata is cached token-free in `~/.atomic/agent/cursor-model-catalog.json` and can be used at startup before fresh credentials are available. Estimated labels are used only when no valid cache exists and allowed live `GetUsableModels` discovery failures occur; refresh-time discovery is best-effort so rotated credentials are still persisted.
|
|
52
|
+
- The implementation avoids a localhost proxy and keeps credentials OAuth-only. Cursor's HTTP/2 transport uses the bundled `@bastani/atomic-natives` Rust/N-API client, so it does not require Node.js on `PATH`. The native client currently opens request-scoped HTTP/2 sessions; pooling may be added in a future release.
|
|
53
|
+
- Cursor request encoding intentionally omits a `previousWorkspaceUris` current-directory entry by default so local absolute working-directory paths are not sent as workspace context. HTTP/2 Connect request/framing code is isolated, buffered across arbitrary chunks, tested with injected fakes, and uses a minimal production protobuf codec with field-order-independent exec ids, protobuf `Value` plus raw UTF-8/JSON tool arguments, historical tool-result correlation, checkpoint token-details parsing, paused-stream abort/idle cleanup, catalog-aware fast/thinking model grouping, and credential/PKCE-redacted protocol errors.
|
|
54
|
+
|
|
55
|
+
Select models as `cursor/<model-id>` (default: `cursor/composer-2`).
|
|
56
|
+
|
|
42
57
|
## API Keys
|
|
43
58
|
|
|
44
59
|
### Environment Variables or Auth File
|
package/docs/sdk.md
CHANGED
|
@@ -622,6 +622,62 @@ Custom tools passed via `customTools` are combined with extension-registered too
|
|
|
622
622
|
|
|
623
623
|
If you pass `tools`, include each custom or extension tool name you want enabled, for example `tools: ["read", "bash", "my_tool"]`. Use `excludedTools` to remove a custom or extension tool by name from the final exposed set.
|
|
624
624
|
|
|
625
|
+
#### Structured output final results
|
|
626
|
+
|
|
627
|
+
`structured_output` is not registered in normal agent sessions by default. Add it only when a caller needs a machine-readable final-answer contract by registering the exported factory as a custom tool:
|
|
628
|
+
|
|
629
|
+
```typescript
|
|
630
|
+
import { Type, type Static } from "typebox";
|
|
631
|
+
import {
|
|
632
|
+
createAgentSession,
|
|
633
|
+
createStructuredOutputTool,
|
|
634
|
+
type StructuredOutputCapture,
|
|
635
|
+
} from "@bastani/atomic";
|
|
636
|
+
|
|
637
|
+
const DecisionSchema = Type.Object({
|
|
638
|
+
approved: Type.Boolean(),
|
|
639
|
+
findings: Type.Array(Type.String()),
|
|
640
|
+
}, { additionalProperties: false });
|
|
641
|
+
|
|
642
|
+
type Decision = Static<typeof DecisionSchema>;
|
|
643
|
+
const capture: StructuredOutputCapture<Decision> = {
|
|
644
|
+
called: false,
|
|
645
|
+
value: undefined,
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
const structuredOutput = createStructuredOutputTool({
|
|
649
|
+
schema: DecisionSchema,
|
|
650
|
+
capture,
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
const { session } = await createAgentSession({
|
|
654
|
+
customTools: [structuredOutput],
|
|
655
|
+
});
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
The tool parameters are exactly `DecisionSchema`: the model calls `structured_output({ approved, findings })`, not `structured_output({ value: { approved, findings } })`. A successful call stores the flat params in `capture.value`, returns them as tool `details`, and sets `terminate: true` so there is no extra follow-up assistant turn. When an `output` file sink is configured, the factory writes the same flat schema-valid params to `output.json` and writes call metadata (`toolName`, `toolCallId`, `success`, `terminate`) to a private `output.meta.json` sidecar for finality-checked parent readback. Structured-output schemas must be top-level object tool-argument schemas; wrap array or primitive final values in object fields such as `{ items: [...] }` or `{ value: ... }`. Structured-output tool definitions opt out of oversized-result persistence, so large final JSON remains inline as the machine-readable result instead of being replaced by a `<persisted-output>` pointer; text print mode also emits the terminating JSON for factory-created tools, including custom names.
|
|
659
|
+
|
|
660
|
+
Custom tool names are supported, and the prompt metadata follows the configured name. If you use a custom name such as `final_decision`, include that name in any explicit `tools` allowlist. If the standard `structured_output` name is required, register the factory with its default name:
|
|
661
|
+
|
|
662
|
+
```typescript
|
|
663
|
+
const finalDecision = createStructuredOutputTool({
|
|
664
|
+
name: "final_decision",
|
|
665
|
+
schema: DecisionSchema,
|
|
666
|
+
capture,
|
|
667
|
+
});
|
|
668
|
+
// The model is prompted to call final_decision exactly once, not structured_output.
|
|
669
|
+
|
|
670
|
+
await createAgentSession({
|
|
671
|
+
customTools: [finalDecision],
|
|
672
|
+
tools: ["final_decision"], // only this tool is enabled
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
await createAgentSession({
|
|
676
|
+
customTools: [createStructuredOutputTool({ schema: DecisionSchema, capture })],
|
|
677
|
+
// Registers the standard structured_output tool for this session only.
|
|
678
|
+
});
|
|
679
|
+
```
|
|
680
|
+
|
|
625
681
|
> See [examples/sdk/05-tools.ts](https://github.com/bastani-inc/atomic/blob/main/packages/coding-agent/examples/sdk/05-tools.ts)
|
|
626
682
|
|
|
627
683
|
### Extensions
|
|
@@ -1182,6 +1238,9 @@ createEventBus
|
|
|
1182
1238
|
|
|
1183
1239
|
// Helpers
|
|
1184
1240
|
defineTool
|
|
1241
|
+
STRUCTURED_OUTPUT_TOOL_NAME
|
|
1242
|
+
createStructuredOutputTool
|
|
1243
|
+
createStructuredOutputCapture
|
|
1185
1244
|
getAgentDir
|
|
1186
1245
|
getPackageDir
|
|
1187
1246
|
getReadmePath
|
|
@@ -1203,6 +1262,8 @@ type CreateAgentSessionOptions
|
|
|
1203
1262
|
type CreateAgentSessionResult
|
|
1204
1263
|
type BashCommandPolicy
|
|
1205
1264
|
type BashCommandRule
|
|
1265
|
+
type StructuredOutputCapture
|
|
1266
|
+
type StructuredOutputToolOptions
|
|
1206
1267
|
type ExtensionFactory
|
|
1207
1268
|
type ExtensionAPI
|
|
1208
1269
|
type ToolDefinition
|
package/docs/security.md
CHANGED
|
@@ -21,7 +21,7 @@ Trusting a project allows Atomic to load trust-gated project inputs, including:
|
|
|
21
21
|
- missing project packages configured through project settings
|
|
22
22
|
- project-local extensions and project package-managed extensions
|
|
23
23
|
|
|
24
|
-
Declining trust skips protected resources. Atomic also skips project-local `AGENTS.md` and `CLAUDE.md` context-file discovery while the project is untrusted; global context and explicitly supplied CLI resources remain available. Before trust is resolved, Atomic only loads user/global extensions and explicit CLI `-e` extensions so those trusted extensions can handle the `project_trust` event; the first extension that returns a yes/no decision owns the decision.
|
|
24
|
+
Declining trust skips protected resources. Atomic also skips project-local `AGENTS.md` and `CLAUDE.md` context-file discovery while the project is untrusted; global context and explicitly supplied CLI resources remain available. Before trust is resolved, Atomic only loads user/global extensions and explicit CLI `-e` package-level extensions so those trusted extensions can handle the `project_trust` event; the first extension that returns a yes/no decision owns the decision. When `-e <dir>` discovers project-local resources borrowed from that directory's `.atomic` or legacy `.pi` config, or from `.agents/skills`, Atomic resolves trust for that extension source before loading those borrowed resources, because borrowed extensions and workflows can execute code with the Atomic process permissions.
|
|
25
25
|
|
|
26
26
|
Non-interactive modes (`-p`, `--mode json`, and `--mode rpc`) do not show a trust prompt. Without an applicable saved trust decision, `defaultProjectTrust: "ask"` and `"never"` ignore such resources, while `"always"` trusts them. Use `--approve`/`-a` or `--no-approve`/`-na` to override project trust for one run.
|
|
27
27
|
|
package/docs/subagents.md
CHANGED
|
@@ -144,6 +144,27 @@ You are a read-only inspector. Inspect the current diff, cite evidence with file
|
|
|
144
144
|
|
|
145
145
|
Use `completionGuard: false` sparingly. It opts a user-authored agent out of automatic completion-guard reminders and is intended for read-only agents whose prompt already prevents premature completion. Do not use it to bypass required implementation or validation work.
|
|
146
146
|
|
|
147
|
+
If an agent or chain step uses an explicit empty `tools: []` allowlist together with `outputSchema`, Atomic starts the child with only `structured_output` enabled for the required final answer. It does not omit `--tools` and accidentally restore default tools. Path-only tool entries remain extension paths and do not create a builtin allowlist by themselves. The child prompt-runtime extension is loaded before user/tool extensions so its schema-backed `structured_output` tool is registered before explicit allowlists are applied.
|
|
148
|
+
|
|
149
|
+
## Structured output schemas
|
|
150
|
+
|
|
151
|
+
Chain and parallel steps can declare an `outputSchema` when the parent needs reliable machine-readable handoff data. Atomic passes that schema to the child as a schema-specific `structured_output` tool backed by the shared Atomic factory. The child must finish by calling `structured_output` exactly once with arguments that match the schema directly:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
structured_output({
|
|
155
|
+
files: ["src/auth.ts"],
|
|
156
|
+
risks: ["missing regression test"],
|
|
157
|
+
})
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`outputSchema` itself must be a top-level object schema because the schema is used directly as the tool's argument contract. Wrap array or primitive handoff values in an explicit object field, such as `{ items: [...] }` or `{ value: ... }`.
|
|
161
|
+
|
|
162
|
+
Do not wrap the payload as `structured_output({ value: ... })` unless your own schema explicitly defines a top-level `value` field. The child runtime writes the flat schema-valid params to `output.json` and call metadata (`toolName`, `toolCallId`, `success`, `terminate`) to the `output.meta.json` sidecar; the parent validates both files before checking transcript finality. Prose-only completion, missing tool calls, missing sidecar metadata, invalid schema data, duplicate structured-output calls, stale output-file captures, sibling tool calls in the same assistant batch, and any assistant/custom/tool-result messages after the successful structured-output result fail the step. The parent accepts cross-process captures only when the transcript proves the same `structured_output` call was the final successful terminating action, then returns the validated flat value as `result.structuredOutput` and in named-chain references under `outputs.name.structured`.
|
|
163
|
+
|
|
164
|
+
Children without `outputSchema` do not receive `structured_output` from Atomic's default tool registry. They can still use a custom extension-provided terminating tool if you explicitly add one, but validated `result.structuredOutput` is reserved for schema-backed `outputSchema` captures.
|
|
165
|
+
|
|
166
|
+
Dynamic fanout `collect.outputSchema` is different: it validates the collected result array after child runs finish, not a child tool-call argument object. Collection schemas remain general JSON Schemas and may use array roots such as `{ "type": "array", "minItems": 1 }`.
|
|
167
|
+
|
|
147
168
|
## Fallback models
|
|
148
169
|
|
|
149
170
|
Agents can define ordered `fallbackModels` for retryable provider or model failures such as rate limits, quota/auth problems, unavailable models, network timeouts, or 5xx errors. Atomic tries the requested primary model first, then configured fallbacks, and finally appends the current user-selected model as the last fallback candidate when available.
|
package/docs/usage.md
CHANGED
|
@@ -160,6 +160,8 @@ In print mode, Atomic also reads piped stdin and merges it into the initial prom
|
|
|
160
160
|
cat README.md | atomic -p "Summarize this text"
|
|
161
161
|
```
|
|
162
162
|
|
|
163
|
+
When a print-mode turn correctly finishes by calling an opt-in terminating structured-output tool created with `createStructuredOutputTool` (for example from an extension, SDK caller, or workflow item with a schema), stdout contains the tool result's JSON text even though there is no follow-up assistant prose. This also works for custom factory names such as `final_decision`. Large final JSON from structured-output tools is preserved inline rather than redirected to a `<persisted-output>` pointer. Non-terminating or unrelated tool results are not printed as the final response.
|
|
164
|
+
|
|
163
165
|
### Model Options
|
|
164
166
|
|
|
165
167
|
| Option | Description |
|
package/docs/workflows.md
CHANGED
|
@@ -9,7 +9,7 @@ Use a workflow when a task should be repeatable, inspectable, resumable, or spli
|
|
|
9
9
|
**Key capabilities:**
|
|
10
10
|
- **Tracked stages** - Name each step and inspect it in workflow status and graph views
|
|
11
11
|
- **Parallel branches** - Run independent research, review, or implementation branches concurrently
|
|
12
|
-
- **Context handoffs** - Pass summaries, artifacts, files, and structured
|
|
12
|
+
- **Context handoffs** - Pass summaries, artifacts, files, and schema-backed structured results between stages
|
|
13
13
|
- **Human input** - Pause for `ctx.ui.input`, `confirm`, `select`, `editor`, or custom TUI widget decisions during a run
|
|
14
14
|
- **Resumable control** - Interrupt, pause, resume, attach to, or kill workflow runs
|
|
15
15
|
- **Artifacts** - Save large outputs to files instead of pushing everything through model context
|
|
@@ -1100,7 +1100,7 @@ Control-signal probing is fail-closed. When the executor inspects an arbitrary t
|
|
|
1100
1100
|
- Avoid workflow-specific or stage-specific vocabulary that is not explained inside the current prompt.
|
|
1101
1101
|
- Use clear software engineering terminology in self-described prompts.
|
|
1102
1102
|
- Avoid hard-coded regular expressions for condition matching when gating reviews or model outputs.
|
|
1103
|
-
- Prefer
|
|
1103
|
+
- Prefer schema-backed workflow stages (`ctx.stage(..., { schema })`, `ctx.chain` items, or `ctx.parallel` items) for review/gate decisions whenever model output needs to be evaluated; Atomic injects the canonical `structured_output` tool only for those schema-enabled items.
|
|
1104
1104
|
- Treat atomic workflow units as language model stages, not deterministic tools.
|
|
1105
1105
|
- When deterministic gates are needed, create small dedicated stages that instruct a model to run a specific tool or perform a specific check. This keeps gates adaptive to the current codebase while preserving explicit workflow structure.
|
|
1106
1106
|
|
|
@@ -1473,9 +1473,12 @@ Common task/stage options include:
|
|
|
1473
1473
|
- `context: "fresh" | "fork"`, `forkFromSessionFile`
|
|
1474
1474
|
- `model`, `fallbackModels`, `thinkingLevel`, `scopedModels`, `modelRegistry` — `model` and each `fallbackModels` entry accept a `model_name:thinking_effort` reasoning suffix; the standalone `thinkingLevel` is deprecated (see [Reasoning levels](#reasoning-levels))
|
|
1475
1475
|
- `tools`, `noTools`, `customTools`, `mcp: { allow?: string[], deny?: string[] }`, `bashPolicy`
|
|
1476
|
+
- `schema` for a structured final answer from this workflow item
|
|
1476
1477
|
- `output`, `outputMode`, `reads`, `worktree`, `gitWorktreeDir`, `baseBranch`, `maxOutput`, `artifacts`, `sessionDir`, `cwd`, `agentDir`
|
|
1477
1478
|
- advanced host-supplied SDK seams: `authStorage`, `resourceLoader`, `sessionManager`, `settingsManager`, `sessionStartEvent`
|
|
1478
1479
|
|
|
1480
|
+
`schema` is opt-in. When a `ctx.stage` call, `ctx.task` call, `ctx.chain` item, or `ctx.parallel` item includes a top-level object TypeBox/JSON Schema, Atomic registers a schema-specific `structured_output` tool for that item only, appends final-answer instructions, and requires the stage to finish by calling the tool exactly once. The prompt result is the parsed structured value for `ctx.stage(..., { schema }).prompt(...)`; task/chain/parallel results also include `result.structured` and keep `result.text` as formatted JSON for handoffs. Because the result contract is single-use, a schema-backed `StageContext` supports one `prompt()` call; create a new `ctx.stage(..., { schema })` for each additional structured prompt. If the item also uses an explicit `tools` allowlist, Atomic automatically adds `structured_output` to that allowlist. Items without `schema` do not receive `structured_output` from the normal tool registry.
|
|
1481
|
+
|
|
1479
1482
|
`bashPolicy` scopes the built-in `bash` tool for one stage or task. `tools` must still include `"bash"` (or leave it available by default); the policy only narrows command text after the shell tool is exposed. It supports exact strings, `{ prefix }`, command-string `{ glob }`, and `{ regex, flags? }` rules, `default: "allow" | "deny"` (default `"allow"`), `deny` precedence, and `match: "segments" | "whole"` (default `"segments"`). Omitting `bashPolicy`, passing `{}`, or passing a default-allow policy with no `allow`/`deny` rules (including empty arrays or match-only default-allow policies) preserves legacy behavior and does not parse commands; malformed policy shapes such as unknown top-level keys (`denny`, `extra`), non-array `allow`/`deny`, invalid rule objects, invalid regexes, invalid glob bracket ranges, or stateful `g`/`y` regex flags fail closed as `invalid-policy`. Segment mode checks each command in pipelines/chains/substitutions before execution, treats unquoted LF, CRLF, and bare CR as command separators, keeps non-leading Bash `>|` noclobber redirections inside the current command segment, and rejects reserved/compound shell heads, leading redirections, attached command-head redirections, and command heads that are not literal words.
|
|
1480
1483
|
|
|
1481
1484
|
```ts
|
|
@@ -1619,7 +1622,7 @@ Stage prompts should be local contracts, not miniature descriptions of the entir
|
|
|
1619
1622
|
|
|
1620
1623
|
- the stage's current objective and what is out of scope for this stage
|
|
1621
1624
|
- the exact files, artifacts, child outputs, or user inputs it may use
|
|
1622
|
-
- the expected output format or
|
|
1625
|
+
- the expected output format, or the schema it must return when the workflow item is schema-enabled
|
|
1623
1626
|
- the checks, tools, or deterministic commands it should run when relevant
|
|
1624
1627
|
- the success criteria that let this stage stop
|
|
1625
1628
|
|
|
@@ -1735,9 +1738,9 @@ Build validation into the workflow instead of waiting for a final manual check.
|
|
|
1735
1738
|
- reviewer stages: fresh-context reviewers that inspect artifacts and current files
|
|
1736
1739
|
- LLM-as-judge stages: direct scoring, pairwise comparison, or rubric-based grading for subjective outputs
|
|
1737
1740
|
|
|
1738
|
-
Prefer
|
|
1741
|
+
Prefer schema-enabled workflow items for model review and gate decisions. `structured_output` is not available to workflow stages through the normal Atomic tool registry; it is injected only when a `ctx.stage`, `ctx.task`, `ctx.chain` item, or `ctx.parallel` item includes `schema`. Structured-output schemas must be top-level object tool-argument schemas, so wrap array or primitive decisions in object fields such as `{ items: [...] }` or `{ value: ... }`; direct JSON invocations of the `workflow` tool must use a schema with `type: "object"` so invalid array/primitive contracts fail at argument validation instead of later in the stage. Terminating `structured_output` JSON is preserved inline even when it exceeds the normal oversized-tool-result threshold, so workflow code can consume the parsed value instead of a `<persisted-output>` pointer. Do not add the old synthetic `{ value: ... }` wrapper around an object payload unless your schema defines that field, and do not make correctness depend on brittle regular-expression matching against free-form prose such as “looks good”, “approved”, or “PASS”. A schema with explicit booleans/enums, findings arrays, confidence, evidence fields, and error reporting is easier to validate, replay, and safely default to “not approved” when malformed.
|
|
1739
1742
|
|
|
1740
|
-
Use small dedicated model stages for adaptive gates when deterministic code alone cannot decide what to check. For example, a stage can read an artifact, inspect the repo, run a named tool or command, and then emit a structured decision. Keep that stage's prompt narrow: tell it the specific check to perform, the files/tools it may use, and the structured decision it must return.
|
|
1743
|
+
Use small dedicated model stages for adaptive gates when deterministic code alone cannot decide what to check. For example, a stage can read an artifact, inspect the repo, run a named tool or command, and then emit a structured decision by configuring `schema` on that workflow item. Keep that stage's prompt narrow: tell it the specific check to perform, the files/tools it may use, and the structured decision it must return.
|
|
1741
1744
|
|
|
1742
1745
|
When using LLM judges, mitigate bias by defining score anchors, asking for evidence, calibrating against examples, and keeping length/order effects in mind. Track pass rates and failures over time for reusable workflows.
|
|
1743
1746
|
|
|
@@ -1794,5 +1797,5 @@ Good workflows are information-flow systems, not just prompt sequences. Keep sta
|
|
|
1794
1797
|
- Do not call `kill` when the user asks to interrupt or pause resumably.
|
|
1795
1798
|
- Keep stage names readable because they appear in workflow status and UI.
|
|
1796
1799
|
- Do not write stage prompts that depend on hidden workflow-wide awareness; make each model stage locally scoped and self-described.
|
|
1797
|
-
- Do not parse model gate decisions from ad-hoc prose with regular expressions;
|
|
1798
|
-
- Return compact structured output and save large artifacts to files.
|
|
1800
|
+
- Do not parse model gate decisions from ad-hoc prose with regular expressions; configure `schema` on a focused workflow item so Atomic injects the canonical `structured_output` tool for that item.
|
|
1801
|
+
- Return compact structured output for decisions and save large artifacts to files; schema-enabled workflow items preserve final JSON inline, but artifact handoffs should still use files when the next stage does not need the whole payload in context.
|
|
@@ -36,7 +36,7 @@ cp permission-gate.ts ~/.atomic/agent/extensions/
|
|
|
36
36
|
| `questionnaire.ts` | Multi-question input with tab bar navigation between questions |
|
|
37
37
|
| `tool-override.ts` | Override built-in tools (e.g., add logging/access control to `read`) |
|
|
38
38
|
| `dynamic-tools.ts` | Register tools after startup (`session_start`) and at runtime via command, with prompt snippets and tool-specific prompt guidelines |
|
|
39
|
-
| `structured-output.ts` |
|
|
39
|
+
| `structured-output.ts` | Opt-in schema-specific `structured_output` tool using Atomic's canonical terminating output factory |
|
|
40
40
|
| `built-in-tool-renderer.ts` | Custom compact rendering for built-in tools (read, bash, edit, write) while keeping original behavior |
|
|
41
41
|
| `minimal-mode.ts` | Override built-in tool rendering for minimal display (only tool calls, no output in collapsed mode) |
|
|
42
42
|
| `truncated-tool.ts` | Wraps ripgrep with proper output truncation (50KB/2000 lines) |
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "atomic-extension-custom-provider-anthropic",
|
|
3
|
-
"version": "0.79.
|
|
3
|
+
"version": "0.79.3",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "atomic-extension-custom-provider-anthropic",
|
|
9
|
-
"version": "0.79.
|
|
9
|
+
"version": "0.79.3",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@anthropic-ai/sdk": "^0.52.0"
|
|
12
12
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "atomic-extension-gondolin",
|
|
3
|
-
"version": "0.79.
|
|
3
|
+
"version": "0.79.3",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "atomic-extension-gondolin",
|
|
9
|
-
"version": "0.79.
|
|
9
|
+
"version": "0.79.3",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@earendil-works/gondolin": "0.12.0"
|
|
12
12
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "atomic-extension-sandbox",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.3",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "atomic-extension-sandbox",
|
|
9
|
-
"version": "1.9.
|
|
9
|
+
"version": "1.9.3",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@anthropic-ai/sandbox-runtime": "^0.0.26"
|
|
12
12
|
}
|