@aligndottech/cli 0.67.0 → 0.68.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +29 -8
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +5 -2
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/capture.d.ts.map +1 -1
  6. package/dist/commands/capture.js +4 -0
  7. package/dist/commands/capture.js.map +1 -1
  8. package/dist/commands/check.d.ts +1 -5
  9. package/dist/commands/check.d.ts.map +1 -1
  10. package/dist/commands/check.js +1 -33
  11. package/dist/commands/check.js.map +1 -1
  12. package/dist/commands/default-action.d.ts.map +1 -1
  13. package/dist/commands/default-action.js +18 -7
  14. package/dist/commands/default-action.js.map +1 -1
  15. package/dist/commands/mcp.d.ts.map +1 -1
  16. package/dist/commands/mcp.js +2 -0
  17. package/dist/commands/mcp.js.map +1 -1
  18. package/dist/commands/setup.d.ts.map +1 -1
  19. package/dist/commands/setup.js +22 -13
  20. package/dist/commands/setup.js.map +1 -1
  21. package/dist/lib/agent/edit-gate.d.ts +30 -0
  22. package/dist/lib/agent/edit-gate.d.ts.map +1 -0
  23. package/dist/lib/agent/edit-gate.js +67 -0
  24. package/dist/lib/agent/edit-gate.js.map +1 -0
  25. package/dist/lib/agent/edit-text.d.ts +14 -0
  26. package/dist/lib/agent/edit-text.d.ts.map +1 -0
  27. package/dist/lib/agent/edit-text.js +34 -0
  28. package/dist/lib/agent/edit-text.js.map +1 -0
  29. package/dist/lib/agent/engines/claude-code-protocol.d.ts +24 -0
  30. package/dist/lib/agent/engines/claude-code-protocol.d.ts.map +1 -0
  31. package/dist/lib/agent/engines/claude-code-protocol.js +123 -0
  32. package/dist/lib/agent/engines/claude-code-protocol.js.map +1 -0
  33. package/dist/lib/agent/engines/claude-code.d.ts +17 -0
  34. package/dist/lib/agent/engines/claude-code.d.ts.map +1 -0
  35. package/dist/lib/agent/engines/claude-code.js +122 -0
  36. package/dist/lib/agent/engines/claude-code.js.map +1 -0
  37. package/dist/lib/agent/engines/pi-models.d.ts +46 -0
  38. package/dist/lib/agent/engines/pi-models.d.ts.map +1 -0
  39. package/dist/lib/agent/engines/pi-models.js +91 -0
  40. package/dist/lib/agent/engines/pi-models.js.map +1 -0
  41. package/dist/lib/agent/engines/pi-sdk.d.ts +39 -0
  42. package/dist/lib/agent/engines/pi-sdk.d.ts.map +1 -0
  43. package/dist/lib/agent/engines/pi-sdk.js +105 -0
  44. package/dist/lib/agent/engines/pi-sdk.js.map +1 -0
  45. package/dist/lib/agent/engines/pi.d.ts +44 -0
  46. package/dist/lib/agent/engines/pi.d.ts.map +1 -0
  47. package/dist/lib/agent/engines/pi.js +185 -0
  48. package/dist/lib/agent/engines/pi.js.map +1 -0
  49. package/dist/lib/agent/graph-tools.d.ts +23 -0
  50. package/dist/lib/agent/graph-tools.d.ts.map +1 -0
  51. package/dist/lib/agent/graph-tools.js +33 -0
  52. package/dist/lib/agent/graph-tools.js.map +1 -0
  53. package/dist/lib/agent/permission-broker.d.ts +25 -0
  54. package/dist/lib/agent/permission-broker.d.ts.map +1 -0
  55. package/dist/lib/agent/permission-broker.js +47 -0
  56. package/dist/lib/agent/permission-broker.js.map +1 -0
  57. package/dist/lib/agent/run-agent-session.d.ts +44 -0
  58. package/dist/lib/agent/run-agent-session.d.ts.map +1 -0
  59. package/dist/lib/agent/run-agent-session.js +137 -0
  60. package/dist/lib/agent/run-agent-session.js.map +1 -0
  61. package/dist/lib/agent/select-engine.d.ts +28 -0
  62. package/dist/lib/agent/select-engine.d.ts.map +1 -0
  63. package/dist/lib/agent/select-engine.js +14 -0
  64. package/dist/lib/agent/select-engine.js.map +1 -0
  65. package/dist/lib/agent/tui/app.d.ts +11 -0
  66. package/dist/lib/agent/tui/app.d.ts.map +1 -0
  67. package/dist/lib/agent/tui/app.js +122 -0
  68. package/dist/lib/agent/tui/app.js.map +1 -0
  69. package/dist/lib/agent/tui/cards.d.ts +16 -0
  70. package/dist/lib/agent/tui/cards.d.ts.map +1 -0
  71. package/dist/lib/agent/tui/cards.js +46 -0
  72. package/dist/lib/agent/tui/cards.js.map +1 -0
  73. package/dist/lib/agent/tui/controller.d.ts +21 -0
  74. package/dist/lib/agent/tui/controller.d.ts.map +1 -0
  75. package/dist/lib/agent/tui/controller.js +29 -0
  76. package/dist/lib/agent/tui/controller.js.map +1 -0
  77. package/dist/lib/agent/tui/theme.d.ts +11 -0
  78. package/dist/lib/agent/tui/theme.d.ts.map +1 -0
  79. package/dist/lib/agent/tui/theme.js +13 -0
  80. package/dist/lib/agent/tui/theme.js.map +1 -0
  81. package/dist/lib/agent/types.d.ts +81 -0
  82. package/dist/lib/agent/types.d.ts.map +1 -0
  83. package/dist/lib/agent/types.js +2 -0
  84. package/dist/lib/agent/types.js.map +1 -0
  85. package/dist/lib/commit-shape.d.ts +3 -1
  86. package/dist/lib/commit-shape.d.ts.map +1 -1
  87. package/dist/lib/commit-shape.js +8 -2
  88. package/dist/lib/commit-shape.js.map +1 -1
  89. package/dist/lib/config.d.ts +4 -0
  90. package/dist/lib/config.d.ts.map +1 -1
  91. package/dist/lib/config.js +17 -0
  92. package/dist/lib/config.js.map +1 -1
  93. package/dist/lib/doctor.d.ts +1 -1
  94. package/dist/lib/doctor.js +2 -2
  95. package/dist/lib/git.d.ts.map +1 -1
  96. package/dist/lib/git.js +4 -1
  97. package/dist/lib/git.js.map +1 -1
  98. package/dist/lib/launch/mcp-child-env.js +1 -1
  99. package/dist/lib/launch/mcp-child-env.js.map +1 -1
  100. package/dist/lib/launch/pi-state.d.ts.map +1 -1
  101. package/dist/lib/launch/pi-state.js +9 -3
  102. package/dist/lib/launch/pi-state.js.map +1 -1
  103. package/dist/lib/personal-import.d.ts +4 -0
  104. package/dist/lib/personal-import.d.ts.map +1 -1
  105. package/dist/lib/personal-import.js +4 -0
  106. package/dist/lib/personal-import.js.map +1 -1
  107. package/dist/lib/scope-real.d.ts +12 -0
  108. package/dist/lib/scope-real.d.ts.map +1 -1
  109. package/dist/lib/sync/real-env.d.ts +12 -0
  110. package/dist/lib/sync/real-env.d.ts.map +1 -1
  111. package/dist/lib/usage-telemetry.d.ts +1 -1
  112. package/dist/lib/usage-telemetry.d.ts.map +1 -1
  113. package/dist/lib/usage-telemetry.js +20 -2
  114. package/dist/lib/usage-telemetry.js.map +1 -1
  115. package/dist/lib/write-confirm.d.ts +13 -4
  116. package/dist/lib/write-confirm.d.ts.map +1 -1
  117. package/dist/lib/write-confirm.js +29 -10
  118. package/dist/lib/write-confirm.js.map +1 -1
  119. package/package.json +5 -2
@@ -0,0 +1,67 @@
1
+ import { decisionLinkFor } from '../decision-links.js';
2
+ import { proposedChangeText } from './edit-text.js';
3
+ /** Same budget as the advisory hook's retrieval (check.ts RETRIEVAL_TIMEOUT_MS). */
4
+ const RETRIEVAL_MS = 2500;
5
+ const ADJUDICATE_MS = 15_000;
6
+ /** A card's why is one short line: a git-imported summary is the whole commit body. */
7
+ const WHY_MAX = 160;
8
+ function shortWhy(summary) {
9
+ const first = summary?.split('\n').map((l) => l.trim()).find(Boolean);
10
+ if (!first)
11
+ return undefined;
12
+ return first.length > WHY_MAX ? `${first.slice(0, WHY_MAX - 3).trimEnd()}...` : first;
13
+ }
14
+ const TIMED_OUT = Symbol('timed-out');
15
+ function within(ms, work) {
16
+ let timer;
17
+ const late = new Promise((resolve) => { timer = setTimeout(() => resolve(TIMED_OUT), ms); });
18
+ return Promise.race([work, late]).finally(() => clearTimeout(timer));
19
+ }
20
+ export async function gateEdit(ask, deps) {
21
+ // An edit with no path is a shape this gate cannot read (an engine field it does not map).
22
+ // It asks rather than passing unseen.
23
+ const filePath = ask.edit?.file_path ?? '';
24
+ if (!filePath)
25
+ return { kind: 'ask', reason: 'unchecked', cards: [] };
26
+ // A deletion has no new text to search with, but blame still reads the lines it removes.
27
+ const text = proposedChangeText(ask.edit);
28
+ const noRelated = Promise.resolve({ relevant_decisions: [] });
29
+ // Separate budgets: a failed or slow retrieval must not throw away a blame hit, which is the
30
+ // stronger signal and the one that asks the person.
31
+ const ms = deps.retrievalMs ?? RETRIEVAL_MS;
32
+ const settle = (work) => within(ms, work).catch(() => TIMED_OUT);
33
+ const [related, wrote] = await Promise.all([
34
+ settle(text.trim() ? deps.client.checkAlignment(text, filePath, { depth: 'related' }) : noRelated),
35
+ settle(deps.blame(ask.edit, deps.cwd)),
36
+ ]);
37
+ const blameCards = (wrote === TIMED_OUT ? [] : wrote).map((d) => ({ id: d.id, title: d.title, via: 'blame' }));
38
+ const seen = new Set(blameCards.map((c) => c.id));
39
+ const retrievalCards = (related === TIMED_OUT ? [] : related.relevant_decisions ?? [])
40
+ .filter((d) => !seen.has(d.id))
41
+ .map((d) => ({ id: d.id, title: d.title, why: shortWhy(d.summary), url: decisionLinkFor(d), via: 'retrieval' }));
42
+ // A blame hit always reaches the person, and no model verdict can clear it: that the edit
43
+ // rewrites lines a decision wrote is a fact from git, a verdict is a guess about a fragment
44
+ // (measured on fastify #7089, 6 Nov: "not a conflict" let the reversal through). So no model
45
+ // call is spent here either.
46
+ if (blameCards.length)
47
+ return { kind: 'ask', reason: 'rewrites-decided-lines', cards: [...blameCards, ...retrievalCards] };
48
+ // No blame hit. A half that did not finish makes the result "not checked", but what the other
49
+ // half found is still used: retrieval hits are still adjudicated and shown when blame timed out.
50
+ const unfinished = related === TIMED_OUT || wrote === TIMED_OUT;
51
+ if (!retrievalCards.length)
52
+ return unfinished ? { kind: 'allow', cards: [], note: 'not-checked' } : { kind: 'allow', cards: [] };
53
+ // Retrieval alone only finds what is similar. A model may escalate that to a question; it
54
+ // never turns a question into a silent pass, because there is none to clear.
55
+ if (deps.canAdjudicate()) {
56
+ const verdict = await within(deps.adjudicateMs ?? ADJUDICATE_MS, deps.client.checkAlignment(text, filePath, { depth: 'full' })).catch(() => TIMED_OUT);
57
+ if (verdict !== TIMED_OUT && verdict.status === 'conflicting') {
58
+ const cards = (verdict.conflicts ?? []).map((c) => ({ id: c.decision_id, title: c.title, why: c.reason, url: decisionLinkFor(c), via: 'verdict' }));
59
+ return { kind: 'ask', reason: 'conflict', cards };
60
+ }
61
+ // A verdict that never came is not a verdict: keep the cards, and say it was not checked.
62
+ if (verdict === TIMED_OUT || verdict.status === 'unknown')
63
+ return { kind: 'allow', cards: retrievalCards, note: 'not-checked' };
64
+ }
65
+ return unfinished ? { kind: 'allow', cards: retrievalCards, note: 'not-checked' } : { kind: 'allow', cards: retrievalCards };
66
+ }
67
+ //# sourceMappingURL=edit-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-gate.js","sourceRoot":"","sources":["../../../src/lib/agent/edit-gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { HookToolInput } from '../hook-payload.js';
2
+ export type RelatedDecision = {
3
+ id: string;
4
+ title: string;
5
+ summary?: string;
6
+ };
7
+ /**
8
+ * The decisions that wrote the lines this edit replaces: `git blame` the lines, then a keyed lookup
9
+ * of the blamed commits and PRs in the graph. Local mode only (the cloud client has no such lookup yet,
10
+ * so it is skipped). Fail-open: any problem returns [] and the hook behaves as it did before.
11
+ */
12
+ export declare function decisionsThatWroteTheseLines(client: unknown, input: HookToolInput | undefined, cwd: string): Promise<RelatedDecision[]>;
13
+ export declare function proposedChangeText(input?: HookToolInput): string;
14
+ //# sourceMappingURL=edit-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-text.d.ts","sourceRoot":"","sources":["../../../src/lib/agent/edit-text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGxD,MAAM,MAAM,eAAe,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAI9E;;;;GAIG;AACH,wBAAsB,4BAA4B,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAU7I;AAID,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE,aAAa,GAAG,MAAM,CAMhE"}
@@ -0,0 +1,34 @@
1
+ import { findLineWriters, isWriterTitle } from '../blame-attribution.js';
2
+ /**
3
+ * The decisions that wrote the lines this edit replaces: `git blame` the lines, then a keyed lookup
4
+ * of the blamed commits and PRs in the graph. Local mode only (the cloud client has no such lookup yet,
5
+ * so it is skipped). Fail-open: any problem returns [] and the hook behaves as it did before.
6
+ */
7
+ export async function decisionsThatWroteTheseLines(client, input, cwd) {
8
+ try {
9
+ const lookup = client.decisionsBySourceRefs;
10
+ if (typeof lookup !== 'function')
11
+ return [];
12
+ const writers = await findLineWriters(input, cwd);
13
+ if (!writers)
14
+ return [];
15
+ return lookup.call(client, writers.commitShas, writers.prUrls).filter((d) => isWriterTitle(d.title)).slice(0, 5);
16
+ }
17
+ catch {
18
+ return [];
19
+ }
20
+ }
21
+ // The proposed change from a PreToolUse payload: Write sends the full content, Edit a
22
+ // new_string, MultiEdit a list of edits. We check the new text against the graph.
23
+ export function proposedChangeText(input) {
24
+ if (!input)
25
+ return '';
26
+ if (typeof input.content === 'string')
27
+ return input.content;
28
+ if (Array.isArray(input.edits))
29
+ return input.edits.map((e) => e.new_string ?? '').join('\n');
30
+ if (typeof input.new_string === 'string')
31
+ return input.new_string;
32
+ return '';
33
+ }
34
+ //# sourceMappingURL=edit-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-text.js","sourceRoot":"","sources":["../../../src/lib/agent/edit-text.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAMzE;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,MAAe,EAAE,KAAgC,EAAE,GAAW;IAC/G,IAAI,CAAC;QACH,MAAM,MAAM,GAAI,MAAyB,CAAC,qBAAqB,CAAC;QAChE,IAAI,OAAO,MAAM,KAAK,UAAU;YAAE,OAAO,EAAE,CAAC;QAC5C,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAClD,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;QACxB,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACnH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,sFAAsF;AACtF,kFAAkF;AAClF,MAAM,UAAU,kBAAkB,CAAC,KAAqB;IACtD,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC;IAC5D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7F,IAAI,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,UAAU,CAAC;IAClE,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { AgentUiEvent, BrokerVerdict, ToolAsk } from '../types.js';
2
+ /**
3
+ * The wire format of the user's own `claude` in stream-json mode, as probed against 2.1.318.
4
+ * Everything here is pure so a change in Claude Code's format is fixed in one file.
5
+ */
6
+ export interface ParsedLine {
7
+ events: AgentUiEvent[];
8
+ /** The engine saw its system/init line. */
9
+ init?: true;
10
+ /** A can_use_tool request the host must answer. */
11
+ permission?: {
12
+ requestId: string;
13
+ ask: ToolAsk;
14
+ input: Record<string, unknown>;
15
+ };
16
+ }
17
+ /** `manual` + host prompts: every permission question comes to us. Never bypassPermissions. */
18
+ export declare function buildArgs(extra?: string[]): string[];
19
+ export declare const initializeLine: (requestId: string) => string;
20
+ export declare const userMessageLine: (text: string) => string;
21
+ export declare const interruptLine: (requestId: string) => string;
22
+ export declare function controlResponse(requestId: string, verdict: BrokerVerdict, input: Record<string, unknown>): string;
23
+ export declare function parseLine(raw: string): ParsedLine;
24
+ //# sourceMappingURL=claude-code-protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code-protocol.d.ts","sourceRoot":"","sources":["../../../../src/lib/agent/engines/claude-code-protocol.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,OAAO,EAAY,MAAM,aAAa,CAAC;AAElF;;;GAGG;AAEH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,2CAA2C;IAC3C,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,mDAAmD;IACnD,UAAU,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC;CAClF;AAOD,+FAA+F;AAC/F,wBAAgB,SAAS,CAAC,KAAK,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE,CAOxD;AAED,eAAO,MAAM,cAAc,GAAI,WAAW,MAAM,KAAG,MAC2C,CAAC;AAC/F,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,KAAG,MACmB,CAAC;AACnE,eAAO,MAAM,aAAa,GAAI,WAAW,MAAM,KAAG,MAC2C,CAAC;AAE9F,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAGjH;AA8CD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CA4CjD"}
@@ -0,0 +1,123 @@
1
+ const asRecord = (v) => (v && typeof v === 'object' && !Array.isArray(v) ? v : null);
2
+ const str = (v) => (typeof v === 'string' ? v : undefined);
3
+ const line = (o) => `${JSON.stringify(o)}\n`;
4
+ /** `manual` + host prompts: every permission question comes to us. Never bypassPermissions. */
5
+ export function buildArgs(extra = []) {
6
+ return ['-p', '--input-format', 'stream-json', '--output-format', 'stream-json', '--verbose',
7
+ '--include-partial-messages', '--permission-mode', 'manual', '--permission-prompts', 'host',
8
+ '--permission-prompt-tool', 'stdio',
9
+ // The user's own settings only. A repo's .claude/settings.json could otherwise run its hooks and
10
+ // pre-approve tools before Align is asked, and headless claude shows no trust prompt for it.
11
+ '--setting-sources', 'user', ...extra];
12
+ }
13
+ export const initializeLine = (requestId) => line({ type: 'control_request', request_id: requestId, request: { subtype: 'initialize' } });
14
+ export const userMessageLine = (text) => line({ type: 'user', message: { role: 'user', content: text } });
15
+ export const interruptLine = (requestId) => line({ type: 'control_request', request_id: requestId, request: { subtype: 'interrupt' } });
16
+ export function controlResponse(requestId, verdict, input) {
17
+ const response = verdict.allow ? { behavior: 'allow', updatedInput: input } : { behavior: 'deny', message: verdict.message };
18
+ return line({ type: 'control_response', response: { subtype: 'success', request_id: requestId, response } });
19
+ }
20
+ const EDIT_TOOLS = new Set(['Write', 'Edit', 'MultiEdit', 'NotebookEdit']);
21
+ const COMMAND_TOOLS = new Set(['Bash', 'PowerShell']);
22
+ const READ_TOOLS = new Set(['Read', 'Glob', 'Grep', 'LS', 'TodoWrite']);
23
+ function kindOf(tool) {
24
+ if (EDIT_TOOLS.has(tool))
25
+ return 'edit';
26
+ if (COMMAND_TOOLS.has(tool))
27
+ return 'command';
28
+ return READ_TOOLS.has(tool) ? 'read' : 'other';
29
+ }
30
+ /** Only the fields the hook check reads, so the gate sees the same shape from every engine. */
31
+ function toEdit(input) {
32
+ const out = {};
33
+ const path = str(input['file_path']) ?? str(input['notebook_path']);
34
+ if (path !== undefined)
35
+ out.file_path = path;
36
+ if (str(input['content']) !== undefined)
37
+ out.content = str(input['content']);
38
+ if (str(input['old_string']) !== undefined)
39
+ out.old_string = str(input['old_string']);
40
+ const next = str(input['new_string']) ?? str(input['new_source']);
41
+ if (next !== undefined)
42
+ out.new_string = next;
43
+ if (Array.isArray(input['edits']))
44
+ out.edits = input['edits'];
45
+ return out;
46
+ }
47
+ function toAsk(id, tool, input) {
48
+ const kind = kindOf(tool);
49
+ const ask = { id, tool, kind };
50
+ if (kind === 'edit')
51
+ ask.edit = toEdit(input);
52
+ if (kind === 'command')
53
+ ask.command = str(input['command']) ?? '';
54
+ if (kind === 'other')
55
+ ask.detail = JSON.stringify(input);
56
+ return ask;
57
+ }
58
+ function summaryOf(tool, input) {
59
+ const kind = kindOf(tool);
60
+ if (kind === 'command')
61
+ return str(input['command']) ?? tool;
62
+ if (kind === 'edit')
63
+ return str(input['file_path']) ?? tool;
64
+ return tool;
65
+ }
66
+ function contentBlocks(msg) {
67
+ const content = asRecord(msg['message'])?.['content'];
68
+ return Array.isArray(content) ? content.map(asRecord).filter((b) => b !== null) : [];
69
+ }
70
+ export function parseLine(raw) {
71
+ let msg;
72
+ try {
73
+ msg = asRecord(JSON.parse(raw));
74
+ }
75
+ catch {
76
+ return { events: [] };
77
+ }
78
+ if (!msg)
79
+ return { events: [] };
80
+ switch (msg['type']) {
81
+ case 'system':
82
+ return msg['subtype'] === 'init' ? { events: [], init: true } : { events: [] };
83
+ case 'result': {
84
+ // An error result ("Not logged in", a refused request) must show, not read as an empty turn.
85
+ const failed = msg['is_error'] === true ? str(msg['result']) : undefined;
86
+ return { events: failed ? [{ type: 'error', message: failed }, { type: 'turn-end' }] : [{ type: 'turn-end' }] };
87
+ }
88
+ case 'stream_event': {
89
+ const delta = asRecord(asRecord(msg['event'])?.['delta']);
90
+ const text = delta?.['type'] === 'text_delta' ? str(delta['text']) : undefined;
91
+ return { events: text === undefined ? [] : [{ type: 'text', delta: text }] };
92
+ }
93
+ case 'assistant': {
94
+ const events = [];
95
+ for (const b of contentBlocks(msg)) {
96
+ if (b['type'] !== 'tool_use')
97
+ continue;
98
+ const tool = str(b['name']) ?? '';
99
+ events.push({ type: 'tool-start', id: str(b['id']) ?? '', tool, summary: summaryOf(tool, asRecord(b['input']) ?? {}) });
100
+ }
101
+ return { events };
102
+ }
103
+ case 'user': {
104
+ const events = [];
105
+ for (const b of contentBlocks(msg)) {
106
+ if (b['type'] === 'tool_result')
107
+ events.push({ type: 'tool-end', id: str(b['tool_use_id']) ?? '', ok: b['is_error'] !== true });
108
+ }
109
+ return { events };
110
+ }
111
+ case 'control_request': {
112
+ const req = asRecord(msg['request']);
113
+ if (req?.['subtype'] !== 'can_use_tool')
114
+ return { events: [] };
115
+ const input = asRecord(req['input']) ?? {};
116
+ const tool = str(req['tool_name']) ?? '';
117
+ return { events: [], permission: { requestId: str(msg['request_id']) ?? '', ask: toAsk(str(req['tool_use_id']) ?? '', tool, input), input } };
118
+ }
119
+ default:
120
+ return { events: [] };
121
+ }
122
+ }
123
+ //# sourceMappingURL=claude-code-protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code-protocol.js","sourceRoot":"","sources":["../../../../src/lib/agent/engines/claude-code-protocol.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { spawn as nodeSpawn } from 'node:child_process';
2
+ import type { AgentEngine } from '../types.js';
3
+ export interface ClaudeCodeOpts {
4
+ cwd: string;
5
+ bin?: string;
6
+ extraArgs?: string[];
7
+ /** Exactly the child's env. The caller passes the user's startup env so a key Align saved never reaches `claude`. */
8
+ env: Record<string, string | undefined>;
9
+ spawn?: typeof nodeSpawn;
10
+ firstEventTimeoutMs?: number;
11
+ }
12
+ /**
13
+ * Drives the user's own, unmodified `claude` over stream-json. Align never touches its auth or
14
+ * model: it only answers the permission questions `claude` asks the host.
15
+ */
16
+ export declare function createClaudeCodeEngine(opts: ClaudeCodeOpts): AgentEngine;
17
+ //# sourceMappingURL=claude-code.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../../../src/lib/agent/engines/claude-code.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuC,KAAK,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC7F,OAAO,KAAK,EAAE,WAAW,EAAwC,MAAM,aAAa,CAAC;AAGrF,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,qHAAqH;IACrH,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACxC,KAAK,CAAC,EAAE,OAAO,SAAS,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAKD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,cAAc,GAAG,WAAW,CAyGxE"}
@@ -0,0 +1,122 @@
1
+ import { spawn as nodeSpawn } from 'node:child_process';
2
+ import { buildArgs, controlResponse, initializeLine, interruptLine, parseLine, userMessageLine } from './claude-code-protocol.js';
3
+ const NOT_INSTALLED = 'Claude Code is not installed (no `claude` on PATH)';
4
+ const KILL_AFTER_MS = 2000;
5
+ /**
6
+ * Drives the user's own, unmodified `claude` over stream-json. Align never touches its auth or
7
+ * model: it only answers the permission questions `claude` asks the host.
8
+ */
9
+ export function createClaudeCodeEngine(opts) {
10
+ const spawnFn = opts.spawn ?? nodeSpawn;
11
+ const firstEventTimeoutMs = opts.firstEventTimeoutMs ?? 20000;
12
+ let child;
13
+ let exitMessage;
14
+ let pending;
15
+ let emit = () => { };
16
+ let broker = async () => ({ allow: true });
17
+ let stderrTail = '';
18
+ let initTimer;
19
+ let buffer = '';
20
+ let interrupts = 0;
21
+ const send = (text) => { child?.stdin.write(text); };
22
+ async function answer(requestId, ask, input) {
23
+ let verdict;
24
+ try {
25
+ verdict = await broker(ask);
26
+ }
27
+ catch (err) {
28
+ // Never leave `claude` waiting on a question nobody will answer.
29
+ verdict = { allow: false, message: `Align could not check this action (${err instanceof Error ? err.message : String(err)}). Do not retry it.` };
30
+ }
31
+ send(controlResponse(requestId, verdict, input));
32
+ }
33
+ function onLine(raw) {
34
+ if (!raw.trim())
35
+ return;
36
+ const parsed = parseLine(raw);
37
+ if (parsed.init)
38
+ clearTimeout(initTimer);
39
+ for (const e of parsed.events) {
40
+ emit(e);
41
+ if (e.type === 'turn-end') {
42
+ pending?.resolve();
43
+ pending = undefined;
44
+ }
45
+ }
46
+ if (parsed.permission)
47
+ void answer(parsed.permission.requestId, parsed.permission.ask, parsed.permission.input);
48
+ }
49
+ return {
50
+ label: 'Claude Code',
51
+ start(onEvent, onAsk) {
52
+ emit = onEvent;
53
+ broker = onAsk;
54
+ return new Promise((resolve, reject) => {
55
+ const c = spawnFn(opts.bin ?? 'claude', buildArgs(opts.extraArgs), { cwd: opts.cwd, env: opts.env, stdio: ['pipe', 'pipe', 'pipe'] });
56
+ child = c;
57
+ c.stdout.on('data', (d) => {
58
+ buffer += String(d);
59
+ let i;
60
+ while ((i = buffer.indexOf('\n')) >= 0) {
61
+ const l = buffer.slice(0, i);
62
+ buffer = buffer.slice(i + 1);
63
+ onLine(l);
64
+ }
65
+ });
66
+ c.stderr.on('data', (d) => { stderrTail = (stderrTail + String(d)).slice(-500); });
67
+ // A claude that died mid-write breaks the pipe. The 'exit' handler reports the death;
68
+ // an unhandled stream error here would crash Align instead.
69
+ c.stdin.on('error', () => { });
70
+ c.once('error', (err) => {
71
+ const message = err.code === 'ENOENT' ? NOT_INSTALLED : err.message;
72
+ emit({ type: 'error', message });
73
+ reject(new Error(message));
74
+ });
75
+ c.once('exit', (code, signal) => {
76
+ clearTimeout(initTimer);
77
+ exitMessage = `Claude Code exited (${signal ? `signal ${signal}` : `code ${code}`})${stderrTail.trim() ? `: ${stderrTail.trim()}` : ''}`;
78
+ if (pending) {
79
+ emit({ type: 'error', message: exitMessage });
80
+ pending.reject(new Error(exitMessage));
81
+ pending = undefined;
82
+ }
83
+ });
84
+ c.once('spawn', () => {
85
+ send(initializeLine(`init-${Date.now()}`));
86
+ // A claude that never starts ends the run: the waiting turn fails, the child is stopped,
87
+ // and later prompts are refused with the same reason, so the screen never hangs.
88
+ initTimer = setTimeout(() => {
89
+ exitMessage = `Claude Code did not start within ${Math.round(firstEventTimeoutMs / 1000)}s. Run \`claude --version\` to check it works.`;
90
+ emit({ type: 'error', message: exitMessage });
91
+ pending?.reject(new Error(exitMessage));
92
+ pending = undefined;
93
+ c.kill();
94
+ }, firstEventTimeoutMs);
95
+ resolve();
96
+ });
97
+ });
98
+ },
99
+ prompt(text) {
100
+ if (exitMessage)
101
+ return Promise.reject(new Error(exitMessage));
102
+ return new Promise((resolve, reject) => {
103
+ pending = { resolve, reject };
104
+ send(userMessageLine(text));
105
+ });
106
+ },
107
+ // Verified against claude 2.1.318: it answers with a result line (error_during_execution), which
108
+ // ends the turn through the normal path, and the process stays up for the next prompt.
109
+ abort() { send(interruptLine(`int-${++interrupts}`)); },
110
+ close() {
111
+ const c = child;
112
+ if (!c || exitMessage)
113
+ return Promise.resolve();
114
+ return new Promise((resolve) => {
115
+ const killer = setTimeout(() => c.kill(), KILL_AFTER_MS);
116
+ c.once('exit', () => { clearTimeout(killer); resolve(); });
117
+ c.stdin.end();
118
+ });
119
+ },
120
+ };
121
+ }
122
+ //# sourceMappingURL=claude-code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../../../src/lib/agent/engines/claude-code.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { type SavedLlmConfig } from '../../local-llm.js';
2
+ /**
3
+ * Which model the Align engine runs, and the key to run it with. No Pi types here: the result is
4
+ * plain data and pi.ts builds Pi's model from it.
5
+ *
6
+ * Keys come from what the user already gave Align (an exported variable, or a key saved with
7
+ * `align ai`) and travel in-process only (H1): they are never put into process.env, so a child
8
+ * process cannot inherit them.
9
+ *
10
+ * NEVER a subscription login. Anthropic's terms forbid a Claude subscription login in third-party
11
+ * apps, and Pi's other OAuth providers (openai-codex, github-copilot, ...) are partner-only. Nothing
12
+ * here, or in pi.ts, calls or offers Pi's login; an override naming one is refused.
13
+ */
14
+ export interface ResolvedPiModel {
15
+ /** Pi's provider id, which is not always Align's (Align says gemini and grok; Pi says google and xai). */
16
+ provider: string;
17
+ modelId: string;
18
+ apiKey?: string;
19
+ /** Set for Ollama only: its OpenAI-compatible endpoint. */
20
+ baseUrl?: string;
21
+ /** Shown in the header, e.g. "Anthropic claude-sonnet-5-5". */
22
+ label: string;
23
+ }
24
+ /**
25
+ * The default coding-grade model per Align provider, read from Pi 1.5.0's catalogue (the test
26
+ * asserts every entry exists there, so a Pi bump that drops one fails loudly).
27
+ * - anthropic: Sonnet 5.5, the model the engine was built and tried against.
28
+ * - openai: gpt-6.1-sol, the newest non-pro flagship, priced like Sonnet 5.5 ($2/$10 per Mtok).
29
+ * - gemini: gemini-3.1-pro-preview, the only Pro-tier model in the catalogue (the rest are Flash).
30
+ * - grok: grok-4.7, the newest Grok. groq: gpt-oss-120b, its largest tool-calling model.
31
+ * - mistral: mistral-large-4, the newest large model with reasoning. deepseek: deepseek-v4-pro.
32
+ * Other Align providers (OpenRouter, a custom endpoint, the long tail) have no default: pick one
33
+ * with ALIGN_AGENT_MODEL, because a guess at a model on a gateway we cannot enumerate is worse than asking.
34
+ */
35
+ export declare const DEFAULT_PI_MODELS: Record<string, {
36
+ provider: string;
37
+ modelId: string;
38
+ }>;
39
+ export interface ResolvePiModelOptions {
40
+ /** Keys saved by `align ai`. The caller reads them from the config store; they never touch process.env. */
41
+ saved?: SavedLlmConfig;
42
+ /** Injected so tests need no network. Returns the model a reachable Ollama would use, or null. */
43
+ probeOllama?: () => Promise<string | null>;
44
+ }
45
+ export declare function resolvePiModel(opts?: ResolvePiModelOptions): Promise<ResolvedPiModel | undefined>;
46
+ //# sourceMappingURL=pi-models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi-models.d.ts","sourceRoot":"","sources":["../../../../src/lib/agent/engines/pi-models.ts"],"names":[],"mappings":"AACA,OAAO,EAAsD,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE7G;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC9B,0GAA0G;IAC1G,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAQnF,CAAC;AAOF,MAAM,WAAW,qBAAqB;IACpC,2GAA2G;IAC3G,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,kGAAkG;IAClG,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC5C;AAuBD,wBAAsB,cAAc,CAAC,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CA2C3G"}
@@ -0,0 +1,91 @@
1
+ import { NAMED_PROVIDERS, namedProvider } from '../../llm-providers.js';
2
+ import { preferredProvider, probeOllama as probeLocalOllama } from '../../local-llm.js';
3
+ /**
4
+ * The default coding-grade model per Align provider, read from Pi 1.5.0's catalogue (the test
5
+ * asserts every entry exists there, so a Pi bump that drops one fails loudly).
6
+ * - anthropic: Sonnet 5.5, the model the engine was built and tried against.
7
+ * - openai: gpt-6.1-sol, the newest non-pro flagship, priced like Sonnet 5.5 ($2/$10 per Mtok).
8
+ * - gemini: gemini-3.1-pro-preview, the only Pro-tier model in the catalogue (the rest are Flash).
9
+ * - grok: grok-4.7, the newest Grok. groq: gpt-oss-120b, its largest tool-calling model.
10
+ * - mistral: mistral-large-4, the newest large model with reasoning. deepseek: deepseek-v4-pro.
11
+ * Other Align providers (OpenRouter, a custom endpoint, the long tail) have no default: pick one
12
+ * with ALIGN_AGENT_MODEL, because a guess at a model on a gateway we cannot enumerate is worse than asking.
13
+ */
14
+ export const DEFAULT_PI_MODELS = {
15
+ anthropic: { provider: 'anthropic', modelId: 'claude-sonnet-5-5' },
16
+ openai: { provider: 'openai', modelId: 'gpt-6.1-sol' },
17
+ gemini: { provider: 'google', modelId: 'gemini-3.1-pro-preview' },
18
+ grok: { provider: 'xai', modelId: 'grok-4.7' },
19
+ groq: { provider: 'groq', modelId: 'openai/gpt-oss-120b' },
20
+ mistral: { provider: 'mistral', modelId: 'mistral-large-4' },
21
+ deepseek: { provider: 'deepseek', modelId: 'deepseek-v4-pro' },
22
+ };
23
+ /** Pi providers that sign in with a subscription. Refused outright (see the header). */
24
+ const SUBSCRIPTION_PROVIDERS = new Set([
25
+ 'openai-codex', 'openai-chatgpt', 'github-copilot', 'google-gemini-cli', 'google-antigravity',
26
+ ]);
27
+ const label = (alignId, modelId) => `${namedProvider(alignId)?.label ?? alignId} ${modelId}`;
28
+ /** The key for one Align provider: a saved key, or an exported one that Align is allowed to use. */
29
+ function keyFor(alignId, saved, preferred) {
30
+ const p = namedProvider(alignId);
31
+ if (!p)
32
+ return undefined;
33
+ const exported = p.keyEnv.map((n) => process.env[n]).find(Boolean);
34
+ const saveKey = saved.keys[alignId];
35
+ // Same rule as local-llm's credentials(): an exported key beyond the original six may be there for
36
+ // another tool, so it counts only when the user also saved one or chose the provider.
37
+ if (exported && (p.autoFromEnv || preferred === alignId || saveKey))
38
+ return exported;
39
+ return saveKey;
40
+ }
41
+ /** A local Ollama model. `||`, not `??`: OLLAMA_HOST='' must not make a relative URL (local-llm's ollamaHost says why). */
42
+ function ollamaModel(model) {
43
+ const host = (process.env['OLLAMA_HOST'] || 'http://localhost:11434').replace(/\/+$/, '');
44
+ return { provider: 'ollama', modelId: model, apiKey: 'ollama', baseUrl: `${host}/v1`, label: `Ollama ${model}` };
45
+ }
46
+ export async function resolvePiModel(opts = {}) {
47
+ const saved = opts.saved ?? { keys: {} };
48
+ const preferred = preferredProvider(saved);
49
+ const override = process.env['ALIGN_AGENT_MODEL'];
50
+ if (override) {
51
+ const slash = override.indexOf('/');
52
+ if (slash < 1 || slash === override.length - 1) {
53
+ throw new Error(`ALIGN_AGENT_MODEL must be provider/model-id (for example anthropic/claude-sonnet-5-5), got "${override}".`);
54
+ }
55
+ const provider = override.slice(0, slash);
56
+ const modelId = override.slice(slash + 1);
57
+ // Ollama needs no key: the override names the local model to run.
58
+ if (provider === 'ollama')
59
+ return ollamaModel(modelId);
60
+ if (SUBSCRIPTION_PROVIDERS.has(provider)) {
61
+ throw new Error(`ALIGN_AGENT_MODEL names ${provider}, which signs in with a subscription. Align runs on API keys only: use a provider you have a key for.`);
62
+ }
63
+ const alignId = Object.entries(DEFAULT_PI_MODELS).find(([, d]) => d.provider === provider)?.[0] ?? provider;
64
+ const apiKey = namedProvider(alignId) ? keyFor(alignId, saved, preferred) : undefined;
65
+ if (!apiKey)
66
+ throw new Error(`ALIGN_AGENT_MODEL names ${provider}, but there is no key for it. Export its key or run align ai.`);
67
+ return { provider, modelId, apiKey, label: label(alignId, modelId) };
68
+ }
69
+ const probe = opts.probeOllama ?? probeLocalOllama;
70
+ // A user who chose Ollama gets Ollama when it runs, before any exported key: a local choice must
71
+ // not quietly become a paid API call.
72
+ if (preferred === 'ollama') {
73
+ const local = await probe();
74
+ if (local)
75
+ return ollamaModel(local);
76
+ }
77
+ // The preference goes first, then the fixed provider order, so the answer does not depend on which
78
+ // shell variable happens to be set.
79
+ const order = [preferred, ...NAMED_PROVIDERS.map((p) => p.id)].filter((id) => Boolean(id) && id !== 'ollama' && id !== 'custom' && id !== 'openrouter');
80
+ for (const alignId of order) {
81
+ const def = DEFAULT_PI_MODELS[alignId];
82
+ const apiKey = def ? keyFor(alignId, saved, preferred) : undefined;
83
+ if (def && apiKey)
84
+ return { provider: def.provider, modelId: def.modelId, apiKey, label: label(alignId, def.modelId) };
85
+ }
86
+ if (preferred === 'ollama')
87
+ return undefined; // already probed above, and it is not running
88
+ const model = await probe();
89
+ return model ? ollamaModel(model) : undefined;
90
+ }
91
+ //# sourceMappingURL=pi-models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi-models.js","sourceRoot":"","sources":["../../../../src/lib/agent/engines/pi-models.ts"],"names":[],"mappings":"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"}