@smartmemory/compose 0.1.0 → 0.1.2-beta
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/.claude/skills/bug-fix/SKILL.md +143 -0
- package/.claude/skills/compose/SKILL.md +604 -0
- package/.compose-deps.json +89 -0
- package/README.md +14 -3
- package/bin/compose.js +473 -0
- package/contracts/comp-obs-contract.schema.json +362 -0
- package/contracts/cross-model-review-result.json +78 -0
- package/contracts/review-result.json +126 -0
- package/dist/assets/{_baseUniq-CQwX6VLz.js → _baseUniq-D-avYfn5.js} +1 -1
- package/dist/assets/{arc-SxJ2J1sh.js → arc-BC4dfQ-X.js} +1 -1
- package/dist/assets/{architectureDiagram-Q4EWVU46-BykunY1F.js → architectureDiagram-Q4EWVU46-BZmFXnGI.js} +1 -1
- package/dist/assets/{blockDiagram-DXYQGD6D-ohAKBOUw.js → blockDiagram-DXYQGD6D-DlfWSuux.js} +1 -1
- package/dist/assets/{c4Diagram-AHTNJAMY-DBDC3ENB.js → c4Diagram-AHTNJAMY-Y__uJrRx.js} +1 -1
- package/dist/assets/channel-LRG9kHqJ.js +1 -0
- package/dist/assets/{chunk-4BX2VUAB-Cv93Z7uM.js → chunk-4BX2VUAB-BfMePfTp.js} +1 -1
- package/dist/assets/{chunk-4TB4RGXK-DE0WBDkj.js → chunk-4TB4RGXK-BdlMSdEA.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-CE1EXenG.js → chunk-55IACEB6-vrQHZTdv.js} +1 -1
- package/dist/assets/{chunk-EDXVE4YY-DA7Ana6H.js → chunk-EDXVE4YY-B8wioVlW.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-CTDIPA3p.js → chunk-FMBD7UC4-Cd6Hrux2.js} +1 -1
- package/dist/assets/{chunk-OYMX7WX6-uGBaPaTX.js → chunk-OYMX7WX6-CfrhdQXY.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-CYlnXuUO.js → chunk-QZHKN3VN-B9JQerOU.js} +1 -1
- package/dist/assets/{chunk-YZCP3GAM-ojGkzcZK.js → chunk-YZCP3GAM-DFN9X99H.js} +1 -1
- package/dist/assets/classDiagram-6PBFFD2Q-BC9a6pDE.js +1 -0
- package/dist/assets/classDiagram-v2-HSJHXN6E-BC9a6pDE.js +1 -0
- package/dist/assets/clone-dRxgFrBv.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-Bktn9hL-.js → cose-bilkent-S5V4N54A-BAn0ap_E.js} +1 -1
- package/dist/assets/{dagre-KV5264BT-DFaSzuRF.js → dagre-KV5264BT-DyxnVq1g.js} +1 -1
- package/dist/assets/{diagram-5BDNPKRD-DnfmDzEm.js → diagram-5BDNPKRD-XCrzqski.js} +1 -1
- package/dist/assets/{diagram-G4DWMVQ6-Bm8W9YnG.js → diagram-G4DWMVQ6-MBCAXft_.js} +1 -1
- package/dist/assets/{diagram-MMDJMWI5-B5-TSKvp.js → diagram-MMDJMWI5-DbtB2yS6.js} +1 -1
- package/dist/assets/{diagram-TYMM5635-ls4rqlky.js → diagram-TYMM5635-Bb5NzX61.js} +1 -1
- package/dist/assets/{erDiagram-SMLLAGMA-giG6WO-r.js → erDiagram-SMLLAGMA-CpIeCOh2.js} +1 -1
- package/dist/assets/{flowDiagram-DWJPFMVM-XvlUuz-7.js → flowDiagram-DWJPFMVM-CHyoKnhW.js} +1 -1
- package/dist/assets/{ganttDiagram-T4ZO3ILL-hLBV57oV.js → ganttDiagram-T4ZO3ILL-DErKteO_.js} +1 -1
- package/dist/assets/{gitGraphDiagram-UUTBAWPF-BHu3s_Gn.js → gitGraphDiagram-UUTBAWPF-KFVAtj2F.js} +1 -1
- package/dist/assets/{graph-D0Cfv00Y.js → graph-CRnO_ifT.js} +1 -1
- package/dist/assets/index-DKBsEUJ-.css +1 -0
- package/dist/assets/index-DkRKLuNr.js +1144 -0
- package/dist/assets/{infoDiagram-42DDH7IO-DbqRsOo3.js → infoDiagram-42DDH7IO-BZFnuSp5.js} +1 -1
- package/dist/assets/{ishikawaDiagram-UXIWVN3A-DnCdx7zb.js → ishikawaDiagram-UXIWVN3A-4Xe2Szde.js} +1 -1
- package/dist/assets/{journeyDiagram-VCZTEJTY-CfD7eNcP.js → journeyDiagram-VCZTEJTY-CZRByfS-.js} +1 -1
- package/dist/assets/{kanban-definition-6JOO6SKY-BYaO9-mK.js → kanban-definition-6JOO6SKY-B95sk6Fk.js} +1 -1
- package/dist/assets/{layout-Bj72wOEB.js → layout-BqNQzxWT.js} +1 -1
- package/dist/assets/{linear-BRFo114D.js → linear-CUh7qb64.js} +1 -1
- package/dist/assets/{min-GCHnKlJS.js → min-wXgOS3ig.js} +1 -1
- package/dist/assets/{mindmap-definition-QFDTVHPH-n0PMebY4.js → mindmap-definition-QFDTVHPH-DB6iaAbO.js} +1 -1
- package/dist/assets/{pieDiagram-DEJITSTG-pN4CljHF.js → pieDiagram-DEJITSTG-CHkZHrTW.js} +1 -1
- package/dist/assets/{quadrantDiagram-34T5L4WZ-DNoAy8-D.js → quadrantDiagram-34T5L4WZ-DoTEO8e3.js} +1 -1
- package/dist/assets/{requirementDiagram-MS252O5E-BhtY05PT.js → requirementDiagram-MS252O5E-Dn8peXYp.js} +1 -1
- package/dist/assets/{sankeyDiagram-XADWPNL6-B6AD-16A.js → sankeyDiagram-XADWPNL6-DRXs6Ipb.js} +1 -1
- package/dist/assets/{sequenceDiagram-FGHM5R23-DShHM-uk.js → sequenceDiagram-FGHM5R23-wBBYZ0aq.js} +1 -1
- package/dist/assets/{stateDiagram-FHFEXIEX-DMxn7HTo.js → stateDiagram-FHFEXIEX-DPlBNGmf.js} +1 -1
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-BW0ezXb4.js +1 -0
- package/dist/assets/{timeline-definition-GMOUNBTQ-Cdu6uq52.js → timeline-definition-GMOUNBTQ-CbbyTlHk.js} +1 -1
- package/dist/assets/{vennDiagram-DHZGUBPP-CpK29iRe.js → vennDiagram-DHZGUBPP-Bj4GaFfj.js} +1 -1
- package/dist/assets/{wardley-RL74JXVD-BQgSkdcO.js → wardley-RL74JXVD-RtNzq8KU.js} +55 -55
- package/dist/assets/{wardleyDiagram-NUSXRM2D-DJHYev6O.js → wardleyDiagram-NUSXRM2D-CDfE3zSj.js} +1 -1
- package/dist/assets/{xychartDiagram-5P7HB3ND-1d75pbaO.js → xychartDiagram-5P7HB3ND-CZXHHYD5.js} +1 -1
- package/dist/index.html +2 -2
- package/lib/budget-ledger.js +45 -0
- package/lib/bug-bisect.js +292 -0
- package/lib/bug-checkpoint.js +191 -0
- package/lib/bug-escalation.js +306 -0
- package/lib/bug-index-gen.js +136 -0
- package/lib/bug-ledger.js +126 -0
- package/lib/build-stream-schema.js +176 -0
- package/lib/build-stream-writer.js +3 -1
- package/lib/build.js +854 -284
- package/lib/connector-factory-shim.js +167 -0
- package/lib/constants.js +18 -0
- package/lib/debug-discipline.js +176 -27
- package/lib/deps.js +205 -0
- package/lib/health-score.js +4 -4
- package/lib/import.js +26 -13
- package/lib/inject-schema.js +21 -0
- package/lib/new.js +27 -53
- package/lib/result-normalizer.js +160 -144
- package/lib/review-lenses.js +5 -5
- package/lib/review-normalize.js +413 -0
- package/lib/review-prompt.js +163 -0
- package/lib/sections.js +325 -0
- package/lib/step-prompt.js +21 -1
- package/lib/step-validator.js +5 -3
- package/lib/stratum-mcp-client.js +172 -7
- package/package.json +14 -3
- package/pipelines/bug-fix.stratum.yaml +39 -1
- package/pipelines/build.stratum.yaml +28 -45
- package/pipelines/review-fix.stratum.yaml +1 -1
- package/presets/team-review.stratum.yaml +21 -14
- package/server/build-stream-bridge.js +28 -0
- package/server/cc-session-feature-resolver.js +111 -0
- package/server/cc-session-reader.js +327 -0
- package/server/cc-session-watcher.js +318 -0
- package/server/compose-mcp-tools.js +0 -125
- package/server/compose-mcp.js +2 -4
- package/server/contract-diff.js +192 -0
- package/server/decision-event-emit.js +175 -0
- package/server/decision-event-id.js +64 -0
- package/server/decision-events-snapshot.js +166 -0
- package/server/design-routes.js +92 -49
- package/server/drift-axes.js +365 -0
- package/server/drift-emit.js +121 -0
- package/server/gate-log-store.js +102 -0
- package/server/lifecycle-phase-history.js +44 -0
- package/server/open-loops-store.js +102 -0
- package/server/schema-validator.js +49 -0
- package/server/status-emit.js +27 -0
- package/server/status-snapshot.js +218 -0
- package/server/vision-routes.js +332 -4
- package/server/vision-server.js +104 -12
- package/server/vision-store.js +21 -0
- package/dist/assets/channel-DGElom1e.js +0 -1
- package/dist/assets/classDiagram-6PBFFD2Q-KqWP9wWZ.js +0 -1
- package/dist/assets/classDiagram-v2-HSJHXN6E-KqWP9wWZ.js +0 -1
- package/dist/assets/clone-DUJKJXd7.js +0 -1
- package/dist/assets/index-CUd6pFGF.css +0 -1
- package/dist/assets/index-DReRlzZI.js +0 -1144
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-o6PnCs4e.js +0 -1
- package/server/connectors/agent-connector.js +0 -78
- package/server/connectors/claude-sdk-connector.js +0 -198
- package/server/connectors/codex-connector.js +0 -240
- package/server/connectors/connector-discovery.js +0 -18
- package/server/connectors/connector-runtime.js +0 -13
- package/server/connectors/opencode-connector.js +0 -200
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as t,b as r,a,S as s}from"./chunk-OYMX7WX6-uGBaPaTX.js";import{_ as i}from"./index-DReRlzZI.js";import"./chunk-55IACEB6-CE1EXenG.js";import"./chunk-EDXVE4YY-DA7Ana6H.js";var l={parser:a,get db(){return new s(2)},renderer:r,styles:t,init:i(e=>{e.state||(e.state={}),e.state.arrowMarkerAbsolute=e.arrowMarkerAbsolute},"init")};export{l as diagram};
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgentConnector — base class for all agent connectors.
|
|
3
|
-
*
|
|
4
|
-
* @implements {ConnectorDiscovery} — see connector-discovery.js
|
|
5
|
-
* @implements {ConnectorRuntime} — see connector-runtime.js
|
|
6
|
-
*
|
|
7
|
-
* Subclasses implement run(), interrupt(), and isRunning.
|
|
8
|
-
* Duck typing — no enforcement at runtime beyond the throw in run().
|
|
9
|
-
*
|
|
10
|
-
* Message envelope (yielded by run()):
|
|
11
|
-
* { type: 'system', subtype: 'init' | 'complete', agent: string, model?: string }
|
|
12
|
-
* { type: 'assistant', content: string }
|
|
13
|
-
* { type: 'tool_use', tool: string, input: object }
|
|
14
|
-
* { type: 'tool_use_summary', summary: string, output?: string }
|
|
15
|
-
* { type: 'error', message: string }
|
|
16
|
-
*
|
|
17
|
-
* Schema mode: if opts.schema is provided, the connector injects it into the
|
|
18
|
-
* prompt as instructions and yields text output. JSON.parse() happens at the
|
|
19
|
-
* MCP layer (agent-mcp.js), never inside connectors.
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
export class AgentConnector {
|
|
23
|
-
// ── Discovery ──────────────────────────────────────────────────────────────
|
|
24
|
-
|
|
25
|
-
/** @returns {string[]} */
|
|
26
|
-
listModels() { return []; }
|
|
27
|
-
|
|
28
|
-
/** @param {string} _modelId @returns {boolean} */
|
|
29
|
-
supportsModel(_modelId) { return false; }
|
|
30
|
-
|
|
31
|
-
/** @param {string} _sessionId @returns {Promise<object[]>} */
|
|
32
|
-
async loadHistory(_sessionId) { return []; }
|
|
33
|
-
|
|
34
|
-
// ── Runtime ────────────────────────────────────────────────────────────────
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Run a prompt against the agent.
|
|
38
|
-
*
|
|
39
|
-
* @param {string} prompt
|
|
40
|
-
* @param {object} [opts]
|
|
41
|
-
* @param {object} [opts.schema] — JSON Schema → structured output mode
|
|
42
|
-
* @param {string} [opts.modelID] — override model for this run
|
|
43
|
-
* @param {string} [opts.providerID] — provider ID (OpenCode subclasses only)
|
|
44
|
-
* @param {string} [opts.cwd] — working directory
|
|
45
|
-
* @param {string[]} [opts.tools] — restrict available tools
|
|
46
|
-
* @returns {AsyncGenerator}
|
|
47
|
-
*/
|
|
48
|
-
// eslint-disable-next-line require-yield
|
|
49
|
-
async *run(_prompt, _opts = {}) {
|
|
50
|
-
throw new Error(`${this.constructor.name}.run() not implemented`);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** Stop the active run cleanly. No-op if not running. */
|
|
54
|
-
interrupt() {}
|
|
55
|
-
|
|
56
|
-
/** Whether a run is currently active. */
|
|
57
|
-
get isRunning() { return false; }
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Inject a JSON schema into a prompt so the agent knows to return structured JSON.
|
|
62
|
-
* Used by both ClaudeSDKConnector and OpencodeConnector — parsing happens at call site.
|
|
63
|
-
*
|
|
64
|
-
* @param {string} prompt
|
|
65
|
-
* @param {object} schema JSON Schema object
|
|
66
|
-
* @returns {string}
|
|
67
|
-
*/
|
|
68
|
-
export function injectSchema(prompt, schema) {
|
|
69
|
-
return (
|
|
70
|
-
`${prompt}\n\n` +
|
|
71
|
-
`IMPORTANT: After completing the task, include a JSON code block at the very end ` +
|
|
72
|
-
`of your response matching this schema:\n` +
|
|
73
|
-
'```json\n' +
|
|
74
|
-
`${JSON.stringify(schema, null, 2)}\n` +
|
|
75
|
-
'```\n' +
|
|
76
|
-
`The JSON block must be the last thing in your response.`
|
|
77
|
-
);
|
|
78
|
-
}
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ClaudeSDKConnector — wraps @anthropic-ai/claude-agent-sdk query().
|
|
3
|
-
*
|
|
4
|
-
* All Anthropic model execution goes through this connector.
|
|
5
|
-
* Yields the same typed message envelope as the other connectors.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
9
|
-
import { AgentConnector, injectSchema } from './agent-connector.js';
|
|
10
|
-
|
|
11
|
-
const DEFAULT_MODEL = process.env.CLAUDE_MODEL || 'claude-sonnet-4-6';
|
|
12
|
-
|
|
13
|
-
export class ClaudeSDKConnector extends AgentConnector {
|
|
14
|
-
// ── Discovery ──────────────────────────────────────────────────────────────
|
|
15
|
-
// No overrides — inherits stubs from AgentConnector. See agent-connector.js.
|
|
16
|
-
|
|
17
|
-
#model;
|
|
18
|
-
#cwd;
|
|
19
|
-
#query = null;
|
|
20
|
-
|
|
21
|
-
#allowedTools;
|
|
22
|
-
#disallowedTools;
|
|
23
|
-
#thinking;
|
|
24
|
-
#effort;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* @param {object} [opts]
|
|
28
|
-
* @param {string} [opts.model] — default model (env CLAUDE_MODEL or claude-sonnet-4-6)
|
|
29
|
-
* @param {string} [opts.cwd] — default working directory
|
|
30
|
-
* @param {string[]} [opts.allowedTools] — restrict to these tools (overrides preset)
|
|
31
|
-
* @param {string[]} [opts.disallowedTools] — deny these tools (used alongside allowedTools or preset)
|
|
32
|
-
* @param {object|null} [opts.thinking] — Claude thinking config, e.g. { type: 'adaptive' } or { type: 'disabled' }
|
|
33
|
-
* @param {string|null} [opts.effort] — effort level: 'low' | 'medium' | 'high' | 'xhigh' | 'max'
|
|
34
|
-
*/
|
|
35
|
-
constructor({ model = DEFAULT_MODEL, cwd = process.cwd(), allowedTools, disallowedTools, thinking, effort } = {}) {
|
|
36
|
-
super();
|
|
37
|
-
this.#model = model;
|
|
38
|
-
this.#cwd = cwd;
|
|
39
|
-
this.#allowedTools = allowedTools ?? null;
|
|
40
|
-
this.#disallowedTools = disallowedTools ?? null;
|
|
41
|
-
this.#thinking = thinking ?? null;
|
|
42
|
-
this.#effort = effort ?? null;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// ── Runtime ────────────────────────────────────────────────────────────────
|
|
46
|
-
|
|
47
|
-
async *run(prompt, { schema, modelID, cwd, thinking, effort } = {}) {
|
|
48
|
-
if (this.#query) {
|
|
49
|
-
throw new Error('ClaudeSDKConnector: run() already active. Call interrupt() first.');
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const actualPrompt = schema ? injectSchema(prompt, schema) : prompt;
|
|
53
|
-
|
|
54
|
-
// Strip CLAUDECODE env var to allow spawning inside a Claude Code session
|
|
55
|
-
const cleanEnv = { ...process.env };
|
|
56
|
-
delete cleanEnv.CLAUDECODE;
|
|
57
|
-
|
|
58
|
-
// Build tools config: prefer explicit allow/deny lists if provided,
|
|
59
|
-
// otherwise fall back to the default claude_code preset (backward compat).
|
|
60
|
-
let toolsConfig;
|
|
61
|
-
if (this.#allowedTools !== null) {
|
|
62
|
-
toolsConfig = { type: 'allowed', allowedTools: this.#allowedTools };
|
|
63
|
-
if (this.#disallowedTools !== null) {
|
|
64
|
-
toolsConfig.disallowedTools = this.#disallowedTools;
|
|
65
|
-
}
|
|
66
|
-
} else if (this.#disallowedTools !== null) {
|
|
67
|
-
toolsConfig = { type: 'preset', preset: 'claude_code', disallowedTools: this.#disallowedTools };
|
|
68
|
-
} else {
|
|
69
|
-
toolsConfig = { type: 'preset', preset: 'claude_code' };
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Resolve thinking/effort: per-run override beats constructor default.
|
|
73
|
-
const resolvedThinking = thinking !== undefined ? thinking : this.#thinking;
|
|
74
|
-
const resolvedEffort = effort !== undefined ? effort : this.#effort;
|
|
75
|
-
|
|
76
|
-
const sdkOptions = {
|
|
77
|
-
cwd: cwd ?? this.#cwd,
|
|
78
|
-
model: modelID ?? this.#model,
|
|
79
|
-
permissionMode: 'acceptEdits',
|
|
80
|
-
tools: toolsConfig,
|
|
81
|
-
env: cleanEnv,
|
|
82
|
-
};
|
|
83
|
-
if (resolvedThinking !== null && resolvedThinking !== undefined) {
|
|
84
|
-
sdkOptions.thinking = resolvedThinking;
|
|
85
|
-
}
|
|
86
|
-
if (resolvedEffort !== null && resolvedEffort !== undefined) {
|
|
87
|
-
sdkOptions.effort = resolvedEffort;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const q = query({
|
|
91
|
-
prompt: actualPrompt,
|
|
92
|
-
options: sdkOptions,
|
|
93
|
-
});
|
|
94
|
-
this.#query = q;
|
|
95
|
-
|
|
96
|
-
yield { type: 'system', subtype: 'init', agent: 'claude', model: modelID ?? this.#model };
|
|
97
|
-
|
|
98
|
-
const activeModel = modelID ?? this.#model;
|
|
99
|
-
try {
|
|
100
|
-
for await (const msg of q) {
|
|
101
|
-
if (process.env.COMPOSE_DEBUG) {
|
|
102
|
-
process.stderr.write(` [sdk] ${msg?.type ?? typeof msg}\n`);
|
|
103
|
-
}
|
|
104
|
-
for (const event of _normalizeAll(msg)) {
|
|
105
|
-
// Inject model into usage events extracted from result messages
|
|
106
|
-
if (event.type === 'usage' && event._from_result) {
|
|
107
|
-
const { _from_result: _, ...usageEvent } = event;
|
|
108
|
-
yield { ...usageEvent, model: activeModel };
|
|
109
|
-
} else {
|
|
110
|
-
yield event;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
yield { type: 'system', subtype: 'complete', agent: 'claude' };
|
|
115
|
-
} catch (err) {
|
|
116
|
-
if (err?.name !== 'AbortError') {
|
|
117
|
-
yield { type: 'error', message: err.message || String(err) };
|
|
118
|
-
}
|
|
119
|
-
} finally {
|
|
120
|
-
this.#query = null;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
interrupt() {
|
|
125
|
-
if (this.#query) {
|
|
126
|
-
try { this.#query.interrupt(); } catch { /* already done */ }
|
|
127
|
-
this.#query = null;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
get isRunning() {
|
|
132
|
-
return this.#query !== null;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// ---------------------------------------------------------------------------
|
|
137
|
-
// Normalize Claude SDK message → shared envelope
|
|
138
|
-
// ---------------------------------------------------------------------------
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Normalize an SDK message into one or more envelope events.
|
|
142
|
-
* Returns an array because a single assistant message can contain
|
|
143
|
-
* multiple content blocks (text + tool_use).
|
|
144
|
-
*/
|
|
145
|
-
function _normalizeAll(msg) {
|
|
146
|
-
if (!msg || typeof msg !== 'object') {
|
|
147
|
-
return [{ type: 'assistant', content: String(msg) }];
|
|
148
|
-
}
|
|
149
|
-
if (msg.type === 'system' || msg.type === 'error') return [msg];
|
|
150
|
-
|
|
151
|
-
// SDK assistant message — extract content blocks from msg.message
|
|
152
|
-
if (msg.type === 'assistant' && msg.message?.content) {
|
|
153
|
-
const events = [];
|
|
154
|
-
for (const block of msg.message.content) {
|
|
155
|
-
if (block.type === 'text' && block.text) {
|
|
156
|
-
events.push({ type: 'assistant', content: block.text });
|
|
157
|
-
} else if (block.type === 'tool_use') {
|
|
158
|
-
events.push({ type: 'tool_use', tool: block.name, input: block.input ?? {} });
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
return events.length > 0 ? events : [msg];
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// SDK result message — contains the final aggregated text and usage metadata
|
|
165
|
-
if (msg.type === 'result' && msg.result) {
|
|
166
|
-
const events = [{ type: 'result', content: msg.result }];
|
|
167
|
-
// Extract usage from msg.usage or msg.message.usage
|
|
168
|
-
const usage = msg.usage ?? msg.message?.usage;
|
|
169
|
-
if (usage) {
|
|
170
|
-
events.push({
|
|
171
|
-
type: 'usage',
|
|
172
|
-
input_tokens: usage.input_tokens ?? 0,
|
|
173
|
-
output_tokens: usage.output_tokens ?? 0,
|
|
174
|
-
cache_creation_input_tokens: usage.cache_creation_input_tokens ?? 0,
|
|
175
|
-
cache_read_input_tokens: usage.cache_read_input_tokens ?? 0,
|
|
176
|
-
// model is injected by the run() loop via _modelForUsage tag on the event
|
|
177
|
-
_from_result: true,
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
return events;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (msg.type === 'tool_use') {
|
|
184
|
-
return [{ type: 'tool_use', tool: msg.name ?? msg.tool, input: msg.input ?? {} }];
|
|
185
|
-
}
|
|
186
|
-
if (msg.type === 'tool_use_summary') {
|
|
187
|
-
const output = (msg.result ?? msg.output ?? '');
|
|
188
|
-
return [{ type: 'tool_use_summary', summary: msg.summary, output: output ? output.slice(0, 2048) : undefined }];
|
|
189
|
-
}
|
|
190
|
-
if (msg.type === 'tool_progress') {
|
|
191
|
-
return [{ type: 'tool_progress', tool: msg.tool_name, elapsed: msg.elapsed_time_seconds }];
|
|
192
|
-
}
|
|
193
|
-
// Delta or text content
|
|
194
|
-
if (msg.delta?.text) return [{ type: 'assistant', content: msg.delta.text }];
|
|
195
|
-
if (msg.content && typeof msg.content === 'string') return [{ type: 'assistant', content: msg.content }];
|
|
196
|
-
// Pass through unknown message types as-is
|
|
197
|
-
return [msg];
|
|
198
|
-
}
|
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CodexConnector — spawns the official `codex exec --json` CLI for each prompt.
|
|
3
|
-
*
|
|
4
|
-
* Replaces the previous opencode-backed implementation. Uses the OpenAI Codex
|
|
5
|
-
* CLI (`codex`, installed via `npm i -g @openai/codex` or `brew install codex`)
|
|
6
|
-
* which streams structured JSONL events to stdout.
|
|
7
|
-
*
|
|
8
|
-
* Auth: run `codex login` once (ChatGPT OAuth), or set OPENAI_API_KEY.
|
|
9
|
-
*
|
|
10
|
-
* Model IDs use the form `<model>` or `<model>/<effort>` where effort is one
|
|
11
|
-
* of `minimal|low|medium|high|xhigh`. The effort suffix is split off and
|
|
12
|
-
* passed as `-c model_reasoning_effort=<effort>`.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { spawn } from 'node:child_process';
|
|
16
|
-
import { createInterface } from 'node:readline';
|
|
17
|
-
import { AgentConnector, injectSchema } from './agent-connector.js';
|
|
18
|
-
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
// Supported Codex model IDs (model + optional /effort suffix)
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
|
-
|
|
23
|
-
export const CODEX_MODEL_IDS = new Set([
|
|
24
|
-
'gpt-5.4',
|
|
25
|
-
'gpt-5.4/low',
|
|
26
|
-
'gpt-5.4/medium',
|
|
27
|
-
'gpt-5.4/high',
|
|
28
|
-
'gpt-5.4/xhigh',
|
|
29
|
-
'gpt-5.2-codex',
|
|
30
|
-
'gpt-5.2-codex/low',
|
|
31
|
-
'gpt-5.2-codex/medium',
|
|
32
|
-
'gpt-5.2-codex/high',
|
|
33
|
-
'gpt-5.2-codex/xhigh',
|
|
34
|
-
'gpt-5.1-codex-max',
|
|
35
|
-
'gpt-5.1-codex-max/low',
|
|
36
|
-
'gpt-5.1-codex-max/medium',
|
|
37
|
-
'gpt-5.1-codex-max/high',
|
|
38
|
-
'gpt-5.1-codex-max/xhigh',
|
|
39
|
-
'gpt-5.1-codex',
|
|
40
|
-
'gpt-5.1-codex/low',
|
|
41
|
-
'gpt-5.1-codex/medium',
|
|
42
|
-
'gpt-5.1-codex/high',
|
|
43
|
-
'gpt-5.1-codex-mini',
|
|
44
|
-
'gpt-5.1-codex-mini/medium',
|
|
45
|
-
'gpt-5.1-codex-mini/high',
|
|
46
|
-
]);
|
|
47
|
-
|
|
48
|
-
const DEFAULT_MODEL_ID = process.env.CODEX_MODEL || 'gpt-5.4';
|
|
49
|
-
const AGENT_NAME = 'codex';
|
|
50
|
-
|
|
51
|
-
// ---------------------------------------------------------------------------
|
|
52
|
-
// CodexConnector
|
|
53
|
-
// ---------------------------------------------------------------------------
|
|
54
|
-
|
|
55
|
-
export class CodexConnector extends AgentConnector {
|
|
56
|
-
_defaultModelID;
|
|
57
|
-
_cwd;
|
|
58
|
-
#proc = null;
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* @param {object} [opts]
|
|
62
|
-
* @param {string} [opts.modelID] — Codex model ID; must be in CODEX_MODEL_IDS
|
|
63
|
-
* @param {string} [opts.cwd] — default working directory
|
|
64
|
-
* @throws {Error} if modelID is not a recognized Codex model
|
|
65
|
-
*/
|
|
66
|
-
constructor({ modelID = DEFAULT_MODEL_ID, cwd = process.cwd() } = {}) {
|
|
67
|
-
super();
|
|
68
|
-
_assertCodexModel(modelID);
|
|
69
|
-
this._defaultModelID = modelID;
|
|
70
|
-
this._cwd = cwd;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// ── Runtime ────────────────────────────────────────────────────────────────
|
|
74
|
-
|
|
75
|
-
async *run(prompt, { schema, modelID, cwd } = {}) {
|
|
76
|
-
if (this.#proc) {
|
|
77
|
-
throw new Error(`${AGENT_NAME}: run() already active. Call interrupt() first.`);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const resolvedModelID = modelID ?? this._defaultModelID;
|
|
81
|
-
_assertCodexModel(resolvedModelID);
|
|
82
|
-
const resolvedCwd = cwd ?? this._cwd;
|
|
83
|
-
const actualPrompt = schema ? injectSchema(prompt, schema) : prompt;
|
|
84
|
-
|
|
85
|
-
const [baseModel, effort] = resolvedModelID.split('/');
|
|
86
|
-
|
|
87
|
-
yield {
|
|
88
|
-
type: 'system', subtype: 'init',
|
|
89
|
-
agent: AGENT_NAME, model: resolvedModelID,
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const args = [
|
|
93
|
-
'exec',
|
|
94
|
-
'--json',
|
|
95
|
-
'--skip-git-repo-check',
|
|
96
|
-
'--sandbox', 'read-only',
|
|
97
|
-
'-m', baseModel,
|
|
98
|
-
'-C', resolvedCwd,
|
|
99
|
-
];
|
|
100
|
-
if (effort) {
|
|
101
|
-
args.push('-c', `model_reasoning_effort="${effort}"`);
|
|
102
|
-
}
|
|
103
|
-
args.push('-'); // read prompt from stdin
|
|
104
|
-
|
|
105
|
-
const proc = spawn('codex', args, {
|
|
106
|
-
cwd: resolvedCwd,
|
|
107
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
108
|
-
env: process.env,
|
|
109
|
-
});
|
|
110
|
-
this.#proc = proc;
|
|
111
|
-
|
|
112
|
-
// Write prompt via stdin to avoid argv length and quoting issues
|
|
113
|
-
proc.stdin.end(actualPrompt);
|
|
114
|
-
|
|
115
|
-
const rl = createInterface({ input: proc.stdout, crlfDelay: Infinity });
|
|
116
|
-
const textParts = [];
|
|
117
|
-
const stderrChunks = [];
|
|
118
|
-
|
|
119
|
-
// Stream stderr — surface auth/rate-limit errors immediately
|
|
120
|
-
proc.stderr.on('data', chunk => {
|
|
121
|
-
stderrChunks.push(chunk);
|
|
122
|
-
const text = chunk.toString();
|
|
123
|
-
const lower = text.toLowerCase();
|
|
124
|
-
if (lower.includes('rate limit') || lower.includes('rate_limit') ||
|
|
125
|
-
lower.includes('quota') || lower.includes('insufficient_quota') ||
|
|
126
|
-
lower.includes('unauthorized') || lower.includes('401') ||
|
|
127
|
-
lower.includes('403') || lower.includes('authentication') ||
|
|
128
|
-
lower.includes('not logged in') || lower.includes('login required') ||
|
|
129
|
-
lower.includes('billing') || lower.includes('exceeded')) {
|
|
130
|
-
process.stderr.write(`\n⚠ ${AGENT_NAME}: ${text.trim()}\n`);
|
|
131
|
-
process.stderr.write(` → Check login: codex login status\n`);
|
|
132
|
-
process.stderr.write(` → Re-auth: codex login\n\n`);
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
// Stall detection — warn if no stdout events for 120s
|
|
137
|
-
let lastEventAt = Date.now();
|
|
138
|
-
const stallTimer = setInterval(() => {
|
|
139
|
-
const silent = Math.round((Date.now() - lastEventAt) / 1000);
|
|
140
|
-
if (silent >= 120) {
|
|
141
|
-
process.stderr.write(`\n⚠ ${AGENT_NAME}: no response for ${silent}s — may be stalled or rate-limited\n`);
|
|
142
|
-
process.stderr.write(` → Press s to skip, or Ctrl+C to abort\n\n`);
|
|
143
|
-
}
|
|
144
|
-
}, 30_000);
|
|
145
|
-
|
|
146
|
-
try {
|
|
147
|
-
for await (const line of rl) {
|
|
148
|
-
if (!line.trim()) continue;
|
|
149
|
-
lastEventAt = Date.now();
|
|
150
|
-
|
|
151
|
-
let event;
|
|
152
|
-
try { event = JSON.parse(line); } catch { continue; }
|
|
153
|
-
|
|
154
|
-
// codex exec --json event shapes:
|
|
155
|
-
// { type: 'thread.started', thread_id }
|
|
156
|
-
// { type: 'turn.started' }
|
|
157
|
-
// { type: 'item.started' | 'item.updated' | 'item.completed', item: {...} }
|
|
158
|
-
// { type: 'turn.completed', usage: { input_tokens, cached_input_tokens, output_tokens } }
|
|
159
|
-
// { type: 'error', message }
|
|
160
|
-
const t = event.type;
|
|
161
|
-
|
|
162
|
-
if (t === 'item.completed' && event.item) {
|
|
163
|
-
const item = event.item;
|
|
164
|
-
if (item.type === 'agent_message' && item.text) {
|
|
165
|
-
textParts.push(item.text);
|
|
166
|
-
yield { type: 'assistant', content: item.text };
|
|
167
|
-
} else if (item.type === 'command_execution') {
|
|
168
|
-
const cmd = item.command ?? item.input?.command ?? '';
|
|
169
|
-
yield { type: 'tool_use', tool: 'bash', input: { command: cmd } };
|
|
170
|
-
const out = item.aggregated_output ?? item.output ?? '';
|
|
171
|
-
if (out) {
|
|
172
|
-
const short = out.length > 80 ? out.slice(0, 77) + '...' : out;
|
|
173
|
-
yield { type: 'tool_use_summary', summary: short, output: String(out).slice(0, 2048) };
|
|
174
|
-
}
|
|
175
|
-
} else if (item.type === 'file_change') {
|
|
176
|
-
yield { type: 'tool_use', tool: 'edit', input: { path: item.path ?? '' } };
|
|
177
|
-
} else if (item.type === 'reasoning' && item.text) {
|
|
178
|
-
// Surface reasoning as assistant content for visibility
|
|
179
|
-
yield { type: 'assistant', content: item.text };
|
|
180
|
-
}
|
|
181
|
-
} else if (t === 'turn.completed' && event.usage) {
|
|
182
|
-
const u = event.usage;
|
|
183
|
-
yield {
|
|
184
|
-
type: 'usage',
|
|
185
|
-
input_tokens: u.input_tokens ?? 0,
|
|
186
|
-
output_tokens: u.output_tokens ?? 0,
|
|
187
|
-
cache_creation_input_tokens: 0,
|
|
188
|
-
cache_read_input_tokens: u.cached_input_tokens ?? 0,
|
|
189
|
-
cost_usd: 0,
|
|
190
|
-
model: resolvedModelID,
|
|
191
|
-
};
|
|
192
|
-
} else if (t === 'error') {
|
|
193
|
-
yield { type: 'error', message: event.message || 'codex error' };
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const exitCode = await new Promise(resolve => proc.on('close', resolve));
|
|
198
|
-
|
|
199
|
-
if (exitCode !== 0 && textParts.length === 0) {
|
|
200
|
-
const stderr = Buffer.concat(stderrChunks).toString();
|
|
201
|
-
yield { type: 'error', message: stderr || `codex exited with code ${exitCode}` };
|
|
202
|
-
} else {
|
|
203
|
-
const fullText = textParts.join('');
|
|
204
|
-
if (fullText) yield { type: 'result', content: fullText };
|
|
205
|
-
yield { type: 'system', subtype: 'complete', agent: AGENT_NAME };
|
|
206
|
-
}
|
|
207
|
-
} catch (err) {
|
|
208
|
-
if (err?.name !== 'AbortError') {
|
|
209
|
-
yield { type: 'error', message: err.message || String(err) };
|
|
210
|
-
}
|
|
211
|
-
} finally {
|
|
212
|
-
clearInterval(stallTimer);
|
|
213
|
-
this.#proc = null;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
interrupt() {
|
|
218
|
-
if (this.#proc) {
|
|
219
|
-
try { this.#proc.kill('SIGTERM'); } catch { /* ignore */ }
|
|
220
|
-
this.#proc = null;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
get isRunning() {
|
|
225
|
-
return this.#proc !== null;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// ---------------------------------------------------------------------------
|
|
230
|
-
// Guard
|
|
231
|
-
// ---------------------------------------------------------------------------
|
|
232
|
-
|
|
233
|
-
function _assertCodexModel(modelID) {
|
|
234
|
-
if (!CODEX_MODEL_IDS.has(modelID)) {
|
|
235
|
-
throw new Error(
|
|
236
|
-
`CodexConnector: '${modelID}' is not a supported Codex model.\n` +
|
|
237
|
-
`Supported models: ${[...CODEX_MODEL_IDS].join(', ')}`
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
// compose/server/connectors/connector-discovery.js
|
|
2
|
-
/**
|
|
3
|
-
* @interface ConnectorDiscovery
|
|
4
|
-
*
|
|
5
|
-
* Stateless vendor capability contract.
|
|
6
|
-
* Implementations must not hold execution state.
|
|
7
|
-
*
|
|
8
|
-
* All three concrete connectors (ClaudeSDKConnector, CodexConnector, OpencodeConnector)
|
|
9
|
-
* satisfy this interface. Shape verified by test/connector-shape.test.js.
|
|
10
|
-
*/
|
|
11
|
-
export const ConnectorDiscoveryInterface = {
|
|
12
|
-
/** @returns {string[]} model IDs available for this vendor */
|
|
13
|
-
listModels() {},
|
|
14
|
-
/** @param {string} modelId @returns {boolean} */
|
|
15
|
-
supportsModel(_modelId) {},
|
|
16
|
-
/** @param {string} sessionId @returns {Promise<object[]>} message history */
|
|
17
|
-
async loadHistory(_sessionId) { return []; },
|
|
18
|
-
};
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
// compose/server/connectors/connector-runtime.js
|
|
2
|
-
/**
|
|
3
|
-
* @interface ConnectorRuntime
|
|
4
|
-
*
|
|
5
|
-
* Stateful execution contract.
|
|
6
|
-
* See agent-connector.js for the message envelope spec.
|
|
7
|
-
*/
|
|
8
|
-
export const ConnectorRuntimeInterface = {
|
|
9
|
-
/** @yields typed message envelopes */
|
|
10
|
-
async *run(_prompt, _opts) {},
|
|
11
|
-
interrupt() {},
|
|
12
|
-
get isRunning() { return false; },
|
|
13
|
-
};
|