@aligndottech/cli 0.67.1 → 0.68.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -7
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +5 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/capture.d.ts.map +1 -1
- package/dist/commands/capture.js +4 -0
- package/dist/commands/capture.js.map +1 -1
- package/dist/commands/check.d.ts +1 -5
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +1 -33
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/connect-agents.d.ts +8 -18
- package/dist/commands/connect-agents.d.ts.map +1 -1
- package/dist/commands/connect-agents.js +32 -19
- package/dist/commands/connect-agents.js.map +1 -1
- package/dist/commands/default-action.d.ts.map +1 -1
- package/dist/commands/default-action.js +18 -7
- package/dist/commands/default-action.js.map +1 -1
- package/dist/commands/local.js +2 -2
- package/dist/commands/local.js.map +1 -1
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +4 -2
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +24 -15
- package/dist/commands/setup.js.map +1 -1
- package/dist/lib/agent/agent-guidance.d.ts +8 -0
- package/dist/lib/agent/agent-guidance.d.ts.map +1 -0
- package/dist/lib/agent/agent-guidance.js +8 -0
- package/dist/lib/agent/agent-guidance.js.map +1 -0
- package/dist/lib/agent/edit-gate.d.ts +30 -0
- package/dist/lib/agent/edit-gate.d.ts.map +1 -0
- package/dist/lib/agent/edit-gate.js +67 -0
- package/dist/lib/agent/edit-gate.js.map +1 -0
- package/dist/lib/agent/edit-text.d.ts +14 -0
- package/dist/lib/agent/edit-text.d.ts.map +1 -0
- package/dist/lib/agent/edit-text.js +34 -0
- package/dist/lib/agent/edit-text.js.map +1 -0
- package/dist/lib/agent/engines/claude-code-protocol.d.ts +24 -0
- package/dist/lib/agent/engines/claude-code-protocol.d.ts.map +1 -0
- package/dist/lib/agent/engines/claude-code-protocol.js +127 -0
- package/dist/lib/agent/engines/claude-code-protocol.js.map +1 -0
- package/dist/lib/agent/engines/claude-code.d.ts +17 -0
- package/dist/lib/agent/engines/claude-code.d.ts.map +1 -0
- package/dist/lib/agent/engines/claude-code.js +122 -0
- package/dist/lib/agent/engines/claude-code.js.map +1 -0
- package/dist/lib/agent/engines/pi-models.d.ts +46 -0
- package/dist/lib/agent/engines/pi-models.d.ts.map +1 -0
- package/dist/lib/agent/engines/pi-models.js +91 -0
- package/dist/lib/agent/engines/pi-models.js.map +1 -0
- package/dist/lib/agent/engines/pi-sdk.d.ts +39 -0
- package/dist/lib/agent/engines/pi-sdk.d.ts.map +1 -0
- package/dist/lib/agent/engines/pi-sdk.js +105 -0
- package/dist/lib/agent/engines/pi-sdk.js.map +1 -0
- package/dist/lib/agent/engines/pi.d.ts +44 -0
- package/dist/lib/agent/engines/pi.d.ts.map +1 -0
- package/dist/lib/agent/engines/pi.js +189 -0
- package/dist/lib/agent/engines/pi.js.map +1 -0
- package/dist/lib/agent/failure-tail.d.ts +9 -0
- package/dist/lib/agent/failure-tail.d.ts.map +1 -0
- package/dist/lib/agent/failure-tail.js +42 -0
- package/dist/lib/agent/failure-tail.js.map +1 -0
- package/dist/lib/agent/git-runs-programs.d.ts +11 -0
- package/dist/lib/agent/git-runs-programs.d.ts.map +1 -0
- package/dist/lib/agent/git-runs-programs.js +25 -0
- package/dist/lib/agent/git-runs-programs.js.map +1 -0
- package/dist/lib/agent/graph-tools.d.ts +23 -0
- package/dist/lib/agent/graph-tools.d.ts.map +1 -0
- package/dist/lib/agent/graph-tools.js +33 -0
- package/dist/lib/agent/graph-tools.js.map +1 -0
- package/dist/lib/agent/permission-broker.d.ts +27 -0
- package/dist/lib/agent/permission-broker.d.ts.map +1 -0
- package/dist/lib/agent/permission-broker.js +56 -0
- package/dist/lib/agent/permission-broker.js.map +1 -0
- package/dist/lib/agent/read-only-command.d.ts +15 -0
- package/dist/lib/agent/read-only-command.d.ts.map +1 -0
- package/dist/lib/agent/read-only-command.js +102 -0
- package/dist/lib/agent/read-only-command.js.map +1 -0
- package/dist/lib/agent/run-agent-session.d.ts +45 -0
- package/dist/lib/agent/run-agent-session.d.ts.map +1 -0
- package/dist/lib/agent/run-agent-session.js +139 -0
- package/dist/lib/agent/run-agent-session.js.map +1 -0
- package/dist/lib/agent/select-engine.d.ts +28 -0
- package/dist/lib/agent/select-engine.d.ts.map +1 -0
- package/dist/lib/agent/select-engine.js +14 -0
- package/dist/lib/agent/select-engine.js.map +1 -0
- package/dist/lib/agent/tui/app.d.ts +12 -0
- package/dist/lib/agent/tui/app.d.ts.map +1 -0
- package/dist/lib/agent/tui/app.js +122 -0
- package/dist/lib/agent/tui/app.js.map +1 -0
- package/dist/lib/agent/tui/cards.d.ts +17 -0
- package/dist/lib/agent/tui/cards.d.ts.map +1 -0
- package/dist/lib/agent/tui/cards.js +48 -0
- package/dist/lib/agent/tui/cards.js.map +1 -0
- package/dist/lib/agent/tui/controller.d.ts +21 -0
- package/dist/lib/agent/tui/controller.d.ts.map +1 -0
- package/dist/lib/agent/tui/controller.js +29 -0
- package/dist/lib/agent/tui/controller.js.map +1 -0
- package/dist/lib/agent/tui/theme.d.ts +11 -0
- package/dist/lib/agent/tui/theme.d.ts.map +1 -0
- package/dist/lib/agent/tui/theme.js +13 -0
- package/dist/lib/agent/tui/theme.js.map +1 -0
- package/dist/lib/agent/types.d.ts +84 -0
- package/dist/lib/agent/types.d.ts.map +1 -0
- package/dist/lib/agent/types.js +2 -0
- package/dist/lib/agent/types.js.map +1 -0
- package/dist/lib/config.d.ts +4 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +17 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/doctor.d.ts +1 -1
- package/dist/lib/doctor.js +2 -2
- package/dist/lib/launch/config-writes.d.ts +5 -3
- package/dist/lib/launch/config-writes.d.ts.map +1 -1
- package/dist/lib/launch/config-writes.js +39 -8
- package/dist/lib/launch/config-writes.js.map +1 -1
- package/dist/lib/launch/launch.d.ts +4 -0
- package/dist/lib/launch/launch.d.ts.map +1 -1
- package/dist/lib/launch/launch.js +14 -6
- package/dist/lib/launch/launch.js.map +1 -1
- package/dist/lib/launch/mcp-child-env.js +1 -1
- package/dist/lib/launch/mcp-child-env.js.map +1 -1
- package/dist/lib/launch/prompts.d.ts +6 -0
- package/dist/lib/launch/prompts.d.ts.map +1 -1
- package/dist/lib/launch/prompts.js +12 -0
- package/dist/lib/launch/prompts.js.map +1 -1
- package/dist/lib/personal-import.d.ts +4 -0
- package/dist/lib/personal-import.d.ts.map +1 -1
- package/dist/lib/personal-import.js +4 -0
- package/dist/lib/personal-import.js.map +1 -1
- package/dist/lib/scope-real.d.ts +12 -0
- package/dist/lib/scope-real.d.ts.map +1 -1
- package/dist/lib/sync/real-env.d.ts +12 -0
- package/dist/lib/sync/real-env.d.ts.map +1 -1
- package/dist/lib/usage-telemetry.d.ts +1 -1
- package/dist/lib/usage-telemetry.d.ts.map +1 -1
- package/dist/lib/usage-telemetry.js +20 -2
- package/dist/lib/usage-telemetry.js.map +1 -1
- package/dist/lib/write-confirm.d.ts +14 -5
- package/dist/lib/write-confirm.d.ts.map +1 -1
- package/dist/lib/write-confirm.js +29 -10
- package/dist/lib/write-confirm.js.map +1 -1
- package/package.json +5 -2
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type * as PiCodingAgent from '@earendil-works/pi-coding-agent';
|
|
2
|
+
import type { DefaultResourceLoader, ExtensionFactory } from '@earendil-works/pi-coding-agent';
|
|
3
|
+
import type { GraphToolSpec } from '../graph-tools.js';
|
|
4
|
+
import type { AgentEngine, BrokerVerdict, ToolAsk } from '../types.js';
|
|
5
|
+
import type { ResolvedPiModel } from './pi-models.js';
|
|
6
|
+
/**
|
|
7
|
+
* The loader the Align engine runs on. A repository is untrusted input: Pi would otherwise load
|
|
8
|
+
* `<cwd>/.pi/extensions`, skills, prompt templates and themes, run them with the user's full
|
|
9
|
+
* permissions, and read `<cwd>/.pi/settings.json` for more. So none of that is discovered. The
|
|
10
|
+
* settings are in-memory as well, so no project or home settings file reaches the session (the
|
|
11
|
+
* hostile-repo test shows noExtensions already covers the settings route; this is the second
|
|
12
|
+
* lock). Context files (AGENTS.md, CLAUDE.md) stay on: they are text, and the agent should
|
|
13
|
+
* follow a repo's own notes.
|
|
14
|
+
*/
|
|
15
|
+
/** The parts of Pi's SDK the engine uses. */
|
|
16
|
+
export type PiSdk = Pick<typeof PiCodingAgent, 'DefaultResourceLoader' | 'ModelRuntime' | 'createAgentSession' | 'SessionManager' | 'SettingsManager'>;
|
|
17
|
+
export declare function createAlignResourceLoader(o: {
|
|
18
|
+
sdk: PiSdk;
|
|
19
|
+
cwd: string;
|
|
20
|
+
agentDir: string;
|
|
21
|
+
systemPrompt?: string;
|
|
22
|
+
extensionFactories?: ExtensionFactory[];
|
|
23
|
+
}): DefaultResourceLoader;
|
|
24
|
+
type BrokerFn = (ask: ToolAsk) => Promise<BrokerVerdict>;
|
|
25
|
+
/**
|
|
26
|
+
* The one inline extension: every tool call waits on the broker, and a denial blocks the call with
|
|
27
|
+
* the broker's message, which Pi hands back to the model as the tool result. Fails CLOSED: no broker
|
|
28
|
+
* yet (start() not called) or a broker that throws blocks the call, because "I could not ask" must
|
|
29
|
+
* never read as "allowed".
|
|
30
|
+
*/
|
|
31
|
+
export declare function createToolGate(getBroker: () => BrokerFn | undefined, cwd: string): ExtensionFactory;
|
|
32
|
+
export interface PiEngineDeps {
|
|
33
|
+
sdk: PiSdk;
|
|
34
|
+
/** The module boundary tests inject, so nothing calls a provider. */
|
|
35
|
+
createAgentSession?: PiSdk['createAgentSession'];
|
|
36
|
+
}
|
|
37
|
+
export declare function createPiEngine(opts: {
|
|
38
|
+
cwd: string;
|
|
39
|
+
stateDir: string;
|
|
40
|
+
model: ResolvedPiModel;
|
|
41
|
+
tools: GraphToolSpec[];
|
|
42
|
+
}, deps: PiEngineDeps): Promise<AgentEngine>;
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=pi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pi.d.ts","sourceRoot":"","sources":["../../../../src/lib/agent/engines/pi.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,aAAa,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,gBAAgB,EAAkB,MAAM,iCAAiC,CAAC;AAI/G,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAgB,aAAa,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAmBtD;;;;;;;;GAQG;AACH,6CAA6C;AAC7C,MAAM,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,aAAa,EAAE,uBAAuB,GAAG,cAAc,GAAG,oBAAoB,GAAG,gBAAgB,GAAG,iBAAiB,CAAC,CAAC;AAEvJ,wBAAgB,yBAAyB,CAAC,CAAC,EAAE;IAC3C,GAAG,EAAE,KAAK,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACzC,GAAG,qBAAqB,CAYxB;AA0BD,KAAK,QAAQ,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;AAEzD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,QAAQ,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAanG;AAkBD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,KAAK,CAAC;IACX,qEAAqE;IACrE,kBAAkB,CAAC,EAAE,KAAK,CAAC,oBAAoB,CAAC,CAAC;CAClD;AAED,wBAAsB,cAAc,CAClC,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,aAAa,EAAE,CAAA;CAAE,EACvF,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,WAAW,CAAC,CA8FtB"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { join, resolve } from 'node:path';
|
|
2
|
+
import { ASK_ONCE_GUIDANCE } from '../agent-guidance.js';
|
|
3
|
+
import { failureTail } from '../failure-tail.js';
|
|
4
|
+
/**
|
|
5
|
+
* Align's engine on Pi. EVERY Pi type stays in this file: Pi's minors break often, and the gate,
|
|
6
|
+
* the broker and the UI only ever see the port in ../types.ts.
|
|
7
|
+
*/
|
|
8
|
+
const ALIGN_AGENT_SYSTEM_PROMPT = [
|
|
9
|
+
"You are Align's coding agent. You work in the user's repository with read, bash, edit, write, grep, find and ls.",
|
|
10
|
+
'You also have tools that read the team\'s decision graph: align_ask, align_search, align_get_topic_timeline and align_get_related_decisions.',
|
|
11
|
+
'Ask the graph before you change behaviour a past decision may cover. Quote a decision by its title when it matters.',
|
|
12
|
+
ASK_ONCE_GUIDANCE,
|
|
13
|
+
'Every edit is checked against the graph before it is written. If an edit is stopped, read the reason, say what it means, and change course. Do not retry the same edit.',
|
|
14
|
+
'Follow the repository\'s own notes (AGENTS.md, CLAUDE.md). Keep answers short and plain.',
|
|
15
|
+
].join('\n');
|
|
16
|
+
const BUILTIN_TOOLS = ['read', 'bash', 'edit', 'write', 'grep', 'find', 'ls'];
|
|
17
|
+
const READ_TOOLS = new Set(['read', 'grep', 'find', 'ls']);
|
|
18
|
+
export function createAlignResourceLoader(o) {
|
|
19
|
+
return new o.sdk.DefaultResourceLoader({
|
|
20
|
+
cwd: o.cwd,
|
|
21
|
+
agentDir: o.agentDir,
|
|
22
|
+
settingsManager: o.sdk.SettingsManager.inMemory(),
|
|
23
|
+
noExtensions: true,
|
|
24
|
+
noSkills: true,
|
|
25
|
+
noPromptTemplates: true,
|
|
26
|
+
noThemes: true,
|
|
27
|
+
systemPrompt: o.systemPrompt,
|
|
28
|
+
extensionFactories: o.extensionFactories,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const str = (v) => (typeof v === 'string' ? v : undefined);
|
|
32
|
+
/** Pi's tool call, in Align's vocabulary. Paths resolve against the working directory, as Pi's own tools do. */
|
|
33
|
+
function toToolAsk(id, tool, input, cwd) {
|
|
34
|
+
const abs = (p) => (p === undefined ? undefined : resolve(cwd, p));
|
|
35
|
+
if (tool === 'write') {
|
|
36
|
+
const edit = { file_path: abs(str(input['path'])), content: str(input['content']) };
|
|
37
|
+
return { id, tool, kind: 'edit', edit };
|
|
38
|
+
}
|
|
39
|
+
if (tool === 'edit') {
|
|
40
|
+
const edits = Array.isArray(input['edits']) ? input['edits'] : [];
|
|
41
|
+
const edit = {
|
|
42
|
+
file_path: abs(str(input['path'])),
|
|
43
|
+
edits: edits.map((e) => ({ old_string: str(e['oldText']), new_string: str(e['newText']) })),
|
|
44
|
+
};
|
|
45
|
+
return { id, tool, kind: 'edit', edit };
|
|
46
|
+
}
|
|
47
|
+
if (tool === 'bash' || tool === 'powershell')
|
|
48
|
+
return { id, tool, kind: 'command', command: str(input['command']) ?? '' };
|
|
49
|
+
// Align's own graph tools (all named align_*) only read the local graph, so they never interrupt.
|
|
50
|
+
if (READ_TOOLS.has(tool) || tool.startsWith('align_'))
|
|
51
|
+
return { id, tool, kind: 'read' };
|
|
52
|
+
return { id, tool, kind: 'other', detail: JSON.stringify(input) };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The one inline extension: every tool call waits on the broker, and a denial blocks the call with
|
|
56
|
+
* the broker's message, which Pi hands back to the model as the tool result. Fails CLOSED: no broker
|
|
57
|
+
* yet (start() not called) or a broker that throws blocks the call, because "I could not ask" must
|
|
58
|
+
* never read as "allowed".
|
|
59
|
+
*/
|
|
60
|
+
export function createToolGate(getBroker, cwd) {
|
|
61
|
+
return (pi) => {
|
|
62
|
+
pi.on('tool_call', async (event) => {
|
|
63
|
+
try {
|
|
64
|
+
const broker = getBroker();
|
|
65
|
+
if (!broker)
|
|
66
|
+
return { block: true, reason: 'Align has not finished starting its edit check, so this call was stopped.' };
|
|
67
|
+
const verdict = await broker(toToolAsk(event.toolCallId, event.toolName, event.input, cwd));
|
|
68
|
+
return verdict.allow ? undefined : { block: true, reason: verdict.message };
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
return { block: true, reason: `Align's edit check failed (${err instanceof Error ? err.message : String(err)}), so this call was stopped.` };
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function graphToolDefinition(spec) {
|
|
77
|
+
return {
|
|
78
|
+
name: spec.name,
|
|
79
|
+
label: spec.name,
|
|
80
|
+
description: spec.description,
|
|
81
|
+
// The MCP inputSchema is plain JSON Schema, which is what a TypeBox schema is at runtime.
|
|
82
|
+
parameters: spec.parameters,
|
|
83
|
+
async execute(_id, params) {
|
|
84
|
+
const text = await spec.run(params);
|
|
85
|
+
return { content: [{ type: 'text', text }], details: undefined };
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const summaryOf = (args) => str(args?.['command']) ?? str(args?.['path']) ?? str(args?.['file_path']) ?? '';
|
|
90
|
+
export async function createPiEngine(opts, deps) {
|
|
91
|
+
const { sdk } = deps;
|
|
92
|
+
const create = deps.createAgentSession ?? sdk.createAgentSession;
|
|
93
|
+
const agentDir = join(opts.stateDir, 'agent');
|
|
94
|
+
let broker;
|
|
95
|
+
let onEvent = () => { };
|
|
96
|
+
// No network at startup, and no login: the key is set in-process from what the user gave Align.
|
|
97
|
+
// Pi's login is never called, so no subscription sign-in is reachable from here.
|
|
98
|
+
const modelRuntime = await sdk.ModelRuntime.create({
|
|
99
|
+
authPath: join(agentDir, 'auth.json'),
|
|
100
|
+
modelsPath: join(agentDir, 'models.json'),
|
|
101
|
+
refreshOnCreate: false,
|
|
102
|
+
});
|
|
103
|
+
const { provider, modelId, apiKey, baseUrl } = opts.model;
|
|
104
|
+
if (provider === 'ollama') {
|
|
105
|
+
modelRuntime.registerProvider('ollama', {
|
|
106
|
+
baseUrl,
|
|
107
|
+
api: 'openai-completions',
|
|
108
|
+
apiKey: apiKey ?? 'ollama',
|
|
109
|
+
models: [{
|
|
110
|
+
id: modelId, name: modelId, input: ['text'], reasoning: false,
|
|
111
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
112
|
+
contextWindow: 32_768, maxTokens: 8_192,
|
|
113
|
+
}],
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
else if (apiKey) {
|
|
117
|
+
await modelRuntime.setRuntimeApiKey(provider, apiKey);
|
|
118
|
+
}
|
|
119
|
+
const model = modelRuntime.getModel(provider, modelId);
|
|
120
|
+
if (!model)
|
|
121
|
+
throw new Error(`Pi does not know the model ${provider}/${modelId}.`);
|
|
122
|
+
const resourceLoader = createAlignResourceLoader({
|
|
123
|
+
sdk,
|
|
124
|
+
cwd: opts.cwd,
|
|
125
|
+
agentDir,
|
|
126
|
+
systemPrompt: ALIGN_AGENT_SYSTEM_PROMPT,
|
|
127
|
+
extensionFactories: [createToolGate(() => broker, opts.cwd)],
|
|
128
|
+
});
|
|
129
|
+
await resourceLoader.reload();
|
|
130
|
+
const { session } = await create({
|
|
131
|
+
cwd: opts.cwd,
|
|
132
|
+
agentDir,
|
|
133
|
+
model,
|
|
134
|
+
modelRuntime,
|
|
135
|
+
resourceLoader,
|
|
136
|
+
settingsManager: sdk.SettingsManager.inMemory(),
|
|
137
|
+
sessionManager: sdk.SessionManager.inMemory(opts.cwd),
|
|
138
|
+
tools: [...BUILTIN_TOOLS, ...opts.tools.map((t) => t.name)],
|
|
139
|
+
customTools: opts.tools.map(graphToolDefinition),
|
|
140
|
+
});
|
|
141
|
+
let turnEnded = false;
|
|
142
|
+
const endTurn = () => { turnEnded = true; onEvent({ type: 'turn-end' }); };
|
|
143
|
+
return {
|
|
144
|
+
label: opts.model.label,
|
|
145
|
+
async start(emit, ask) {
|
|
146
|
+
onEvent = emit;
|
|
147
|
+
broker = ask;
|
|
148
|
+
session.subscribe((e) => {
|
|
149
|
+
if (e.type === 'message_update') {
|
|
150
|
+
if (e.assistantMessageEvent.type === 'text_delta')
|
|
151
|
+
onEvent({ type: 'text', delta: e.assistantMessageEvent.delta });
|
|
152
|
+
}
|
|
153
|
+
else if (e.type === 'tool_execution_start') {
|
|
154
|
+
onEvent({ type: 'tool-start', id: e.toolCallId, tool: e.toolName, summary: summaryOf(e.args) });
|
|
155
|
+
}
|
|
156
|
+
else if (e.type === 'tool_execution_end') {
|
|
157
|
+
const detail = e.isError ? failureTail(e.result) : undefined;
|
|
158
|
+
onEvent({ type: 'tool-end', id: e.toolCallId, ok: !e.isError, ...(detail === undefined ? {} : { detail }) });
|
|
159
|
+
}
|
|
160
|
+
else if (e.type === 'agent_end' && !e.willRetry) {
|
|
161
|
+
// A provider failure (bad key, no credit) arrives as a message that ended in error, and
|
|
162
|
+
// without this it would read as an empty turn.
|
|
163
|
+
const last = [...e.messages].reverse().find((m) => m['role'] === 'assistant');
|
|
164
|
+
if (last?.['stopReason'] === 'error')
|
|
165
|
+
onEvent({ type: 'error', message: str(last['errorMessage']) ?? 'The model call failed.' });
|
|
166
|
+
endTurn();
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
},
|
|
170
|
+
async prompt(text) {
|
|
171
|
+
turnEnded = false;
|
|
172
|
+
try {
|
|
173
|
+
await session.prompt(text);
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
onEvent({ type: 'error', message: err instanceof Error ? err.message : String(err) });
|
|
177
|
+
if (!turnEnded)
|
|
178
|
+
endTurn();
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
abort() {
|
|
182
|
+
void session.abort();
|
|
183
|
+
},
|
|
184
|
+
async close() {
|
|
185
|
+
session.dispose();
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=pi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pi.js","sourceRoot":"","sources":["../../../../src/lib/agent/engines/pi.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a failed tool call shows the person: the last few lines of what the tool said, which for a
|
|
3
|
+
* shell command carries the exit code and stderr. Engines hand over a result in different shapes
|
|
4
|
+
* (a string, a list of text blocks, an object holding either), so this reads all of them and
|
|
5
|
+
* returns nothing when there is no text, rather than inventing a reason.
|
|
6
|
+
*/
|
|
7
|
+
/** The last few lines of a failed tool's result, safe to print, or undefined when it said nothing. */
|
|
8
|
+
export declare function failureTail(result: unknown): string | undefined;
|
|
9
|
+
//# sourceMappingURL=failure-tail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failure-tail.d.ts","sourceRoot":"","sources":["../../../src/lib/agent/failure-tail.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA+BH,sGAAsG;AACtG,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAI/D"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a failed tool call shows the person: the last few lines of what the tool said, which for a
|
|
3
|
+
* shell command carries the exit code and stderr. Engines hand over a result in different shapes
|
|
4
|
+
* (a string, a list of text blocks, an object holding either), so this reads all of them and
|
|
5
|
+
* returns nothing when there is no text, rather than inventing a reason.
|
|
6
|
+
*/
|
|
7
|
+
const MAX_LINES = 3;
|
|
8
|
+
const MAX_LINE_CHARS = 200;
|
|
9
|
+
/** The text inside a tool result, whatever shape the engine used. */
|
|
10
|
+
function textOf(v) {
|
|
11
|
+
if (typeof v === 'string')
|
|
12
|
+
return v;
|
|
13
|
+
if (Array.isArray(v))
|
|
14
|
+
return v.map(textOf).filter((t) => t !== '').join('\n');
|
|
15
|
+
if (v && typeof v === 'object') {
|
|
16
|
+
const o = v;
|
|
17
|
+
return textOf(typeof o['text'] === 'string' ? o['text'] : o['content']);
|
|
18
|
+
}
|
|
19
|
+
return '';
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Tool output is untrusted text going to the person's terminal. Drop escape sequences (a stray
|
|
23
|
+
* `ESC[2J` would clear the screen) and other control characters, keeping line breaks.
|
|
24
|
+
*/
|
|
25
|
+
function stripTerminalControls(text) {
|
|
26
|
+
/* eslint-disable no-control-regex -- matching control characters is the job here */
|
|
27
|
+
return text
|
|
28
|
+
.replace(/\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)?/g, '')
|
|
29
|
+
.replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, '')
|
|
30
|
+
.replace(/\r\n?/g, '\n')
|
|
31
|
+
.replace(/\t/g, ' ')
|
|
32
|
+
.replace(/[\u0000-\u0009\u000b-\u001f\u007f-\u009f]/g, '');
|
|
33
|
+
/* eslint-enable no-control-regex */
|
|
34
|
+
}
|
|
35
|
+
/** The last few lines of a failed tool's result, safe to print, or undefined when it said nothing. */
|
|
36
|
+
export function failureTail(result) {
|
|
37
|
+
const lines = stripTerminalControls(textOf(result)).split('\n').map((l) => l.trim()).filter((l) => l !== '');
|
|
38
|
+
if (lines.length === 0)
|
|
39
|
+
return undefined;
|
|
40
|
+
return lines.slice(-MAX_LINES).map((l) => (l.length > MAX_LINE_CHARS ? `${l.slice(0, MAX_LINE_CHARS - 1)}…` : l)).join('\n');
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=failure-tail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failure-tail.js","sourceRoot":"","sources":["../../../src/lib/agent/failure-tail.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,SAAS,GAAG,CAAC,CAAC;AACpB,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B,qEAAqE;AACrE,SAAS,MAAM,CAAC,CAAU;IACxB,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC;IACpC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9E,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAG,CAA4B,CAAC;QACvC,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;GAGG;AACH,SAAS,qBAAqB,CAAC,IAAY;IACzC,oFAAoF;IACpF,OAAO,IAAI;SACR,OAAO,CAAC,+CAA+C,EAAE,EAAE,CAAC;SAC5D,OAAO,CAAC,4BAA4B,EAAE,EAAE,CAAC;SACzC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC;SACvB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,4CAA4C,EAAE,EAAE,CAAC,CAAC;IAC7D,oCAAoC;AACtC,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,WAAW,CAAC,MAAe;IACzC,MAAM,KAAK,GAAG,qBAAqB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IAC7G,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACzC,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC/H,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type GitConfigRunner = (args: string[]) => Promise<{
|
|
2
|
+
code: number;
|
|
3
|
+
stdout: string;
|
|
4
|
+
}>;
|
|
5
|
+
/**
|
|
6
|
+
* True when the repository's config names a program git would run, or when that cannot be read.
|
|
7
|
+
* Exit 1 is git's "no match", the only answer that clears it: a missing git, a timeout or a
|
|
8
|
+
* folder that is not a repository all count as "could not look", and that asks.
|
|
9
|
+
*/
|
|
10
|
+
export declare function gitConfigRunsPrograms(cwd: string, run?: GitConfigRunner): Promise<boolean>;
|
|
11
|
+
//# sourceMappingURL=git-runs-programs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-runs-programs.d.ts","sourceRoot":"","sources":["../../../src/lib/agent/git-runs-programs.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAU5F;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,eAA6B,GAAG,OAAO,CAAC,OAAO,CAAC,CAG7G"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
/**
|
|
3
|
+
* Git runs a program for some read commands when the repository's own config names one:
|
|
4
|
+
* `core.fsmonitor` on status and diff, a `textconv` or external diff driver on diff, show and log,
|
|
5
|
+
* and anything pulled in through `include`. A clone does not carry `.git/config`, so a hostile
|
|
6
|
+
* repo cannot set these, but a repo that arrived with its `.git` folder can. The user's own global
|
|
7
|
+
* config is theirs to trust, so only the repository's config is read.
|
|
8
|
+
*/
|
|
9
|
+
const RISKY_KEYS = String.raw `^(core\.fsmonitor|diff\.external|diff\..*\.(textconv|command)|gpg\.(.*\.)?program|include\..*|includeif\..*)$`;
|
|
10
|
+
const runGit = (cwd) => (args) => new Promise((resolve) => {
|
|
11
|
+
execFile('git', args, { cwd, timeout: 3000 }, (err, stdout) => {
|
|
12
|
+
const code = err ? (typeof err.code === 'number' ? err.code : 128) : 0;
|
|
13
|
+
resolve({ code, stdout: String(stdout) });
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* True when the repository's config names a program git would run, or when that cannot be read.
|
|
18
|
+
* Exit 1 is git's "no match", the only answer that clears it: a missing git, a timeout or a
|
|
19
|
+
* folder that is not a repository all count as "could not look", and that asks.
|
|
20
|
+
*/
|
|
21
|
+
export async function gitConfigRunsPrograms(cwd, run = runGit(cwd)) {
|
|
22
|
+
const results = await Promise.all(['--local', '--worktree'].map((scope) => run(['config', scope, '--get-regexp', RISKY_KEYS])));
|
|
23
|
+
return results.some((r) => r.code !== 1);
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=git-runs-programs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-runs-programs.js","sourceRoot":"","sources":["../../../src/lib/agent/git-runs-programs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAA,+GAA+G,CAAC;AAI7I,MAAM,MAAM,GAAG,CAAC,GAAW,EAAmB,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CACxD,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;IACtB,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE;QAC5D,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,OAAQ,GAA0B,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAE,GAAwB,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACrH,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEL;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,GAAW,EAAE,MAAuB,MAAM,CAAC,GAAG,CAAC;IACzF,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,cAAc,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAChI,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC;AAC3C,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { EnvironmentConfig } from '../config.js';
|
|
2
|
+
import type { createGatewayClient } from '../gateway-client.js';
|
|
3
|
+
/**
|
|
4
|
+
* The decision-graph tools the Align engine hands its model, as engine-neutral specs. An engine
|
|
5
|
+
* adapter turns each spec into its own tool type, so no engine's types leak in here.
|
|
6
|
+
*
|
|
7
|
+
* Every tool goes through dispatchTool, the router `align mcp` serves, so an agent asking from
|
|
8
|
+
* inside `align` gets the answers an agent asking over MCP gets. These are the four READ tools
|
|
9
|
+
* an agent reaches for; align_capture and the other writers stay out of the agent's hands.
|
|
10
|
+
*/
|
|
11
|
+
export interface GraphToolSpec {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
/** JSON Schema, copied from TOOL_SCHEMAS so there is one writer of what a tool takes. */
|
|
15
|
+
parameters: Record<string, unknown>;
|
|
16
|
+
run(args: Record<string, unknown>): Promise<string>;
|
|
17
|
+
}
|
|
18
|
+
export interface GraphToolDeps {
|
|
19
|
+
client: ReturnType<typeof createGatewayClient>;
|
|
20
|
+
env: EnvironmentConfig;
|
|
21
|
+
}
|
|
22
|
+
export declare function createGraphTools({ client, env }: GraphToolDeps): GraphToolSpec[];
|
|
23
|
+
//# sourceMappingURL=graph-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-tools.d.ts","sourceRoot":"","sources":["../../../src/lib/agent/graph-tools.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAGhE;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,yFAAyF;IACzF,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;IAC/C,GAAG,EAAE,iBAAiB,CAAC;CACxB;AAID,wBAAgB,gBAAgB,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,aAAa,GAAG,aAAa,EAAE,CA2BhF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { dispatchTool, isFirstUsefulToolResult, serializeMcpResult, TOOL_SCHEMAS } from '../../commands/mcp.js';
|
|
2
|
+
import { recordFunnelStage } from '../usage-telemetry.js';
|
|
3
|
+
const GRAPH_TOOL_NAMES = ['align_ask', 'align_search', 'align_get_topic_timeline', 'align_get_related_decisions'];
|
|
4
|
+
export function createGraphTools({ client, env }) {
|
|
5
|
+
// One flag per tool set, so first_useful_decision is offered once per session. The
|
|
6
|
+
// once-per-install guard is recordFunnelStage's; this only stops a chatty session re-offering.
|
|
7
|
+
let recordedFirstUseful = false;
|
|
8
|
+
// The local graph has no topic timeline (the local client cannot serve it), so the local
|
|
9
|
+
// engine is not offered a tool that would only fail.
|
|
10
|
+
const names = GRAPH_TOOL_NAMES.filter((n) => !(env.mode === 'local-embedded' && n === 'align_get_topic_timeline'));
|
|
11
|
+
return names.map((name) => {
|
|
12
|
+
const schema = TOOL_SCHEMAS.find((s) => s.name === name);
|
|
13
|
+
if (!schema)
|
|
14
|
+
throw new Error(`graph tool ${name} is not in TOOL_SCHEMAS`);
|
|
15
|
+
return {
|
|
16
|
+
name,
|
|
17
|
+
description: schema.description,
|
|
18
|
+
parameters: schema.inputSchema,
|
|
19
|
+
async run(args) {
|
|
20
|
+
const result = await dispatchTool(name, args, client, env);
|
|
21
|
+
if (!recordedFirstUseful && isFirstUsefulToolResult(name, result)) {
|
|
22
|
+
recordedFirstUseful = true;
|
|
23
|
+
// Not awaited: a tool response never waits on telemetry (same as createCallToolHandler).
|
|
24
|
+
void recordFunnelStage(env, 'first_useful_decision', 'mcp');
|
|
25
|
+
}
|
|
26
|
+
// Any successful graph call is use, as in the MCP handler; recordFunnelStage keeps it to once a day.
|
|
27
|
+
void recordFunnelStage(env, 'agent_active', 'mcp');
|
|
28
|
+
return serializeMcpResult(result);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=graph-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-tools.js","sourceRoot":"","sources":["../../../src/lib/agent/graph-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGhH,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAuB1D,MAAM,gBAAgB,GAAG,CAAC,WAAW,EAAE,cAAc,EAAE,0BAA0B,EAAE,6BAA6B,CAAU,CAAC;AAE3H,MAAM,UAAU,gBAAgB,CAAC,EAAE,MAAM,EAAE,GAAG,EAAiB;IAC7D,mFAAmF;IACnF,+FAA+F;IAC/F,IAAI,mBAAmB,GAAG,KAAK,CAAC;IAChC,yFAAyF;IACzF,qDAAqD;IACrD,MAAM,KAAK,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,gBAAgB,IAAI,CAAC,KAAK,0BAA0B,CAAC,CAAC,CAAC;IACnH,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACxB,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;QACzD,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,yBAAyB,CAAC,CAAC;QAC1E,OAAO;YACL,IAAI;YACJ,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,UAAU,EAAE,MAAM,CAAC,WAAsC;YACzD,KAAK,CAAC,GAAG,CAAC,IAAI;gBACZ,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;gBAC3D,IAAI,CAAC,mBAAmB,IAAI,uBAAuB,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;oBAClE,mBAAmB,GAAG,IAAI,CAAC;oBAC3B,yFAAyF;oBACzF,KAAK,iBAAiB,CAAC,GAAG,EAAE,uBAAuB,EAAE,KAAK,CAAC,CAAC;gBAC9D,CAAC;gBACD,qGAAqG;gBACrG,KAAK,iBAAiB,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,CAAC,CAAC;gBACnD,OAAO,kBAAkB,CAAC,MAAM,CAAC,CAAC;YACpC,CAAC;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { BrokerVerdict, GateOutcome, HumanAnswer, ToolAsk } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Who has to approve a tool call, for both engines. Edits go through the edit gate and reach a
|
|
4
|
+
* person only when the gate asks; commands always reach a person unless they allowed that command
|
|
5
|
+
* for this session; reads never do. A denial carries a message written for the MODEL, because the
|
|
6
|
+
* engine hands it back as the tool result - that is how "stop" turns into the agent changing course.
|
|
7
|
+
*/
|
|
8
|
+
export type HumanQuestion = {
|
|
9
|
+
kind: 'gate';
|
|
10
|
+
outcome: Extract<GateOutcome, {
|
|
11
|
+
kind: 'ask';
|
|
12
|
+
}>;
|
|
13
|
+
ask: ToolAsk;
|
|
14
|
+
} | {
|
|
15
|
+
kind: 'command';
|
|
16
|
+
command: string;
|
|
17
|
+
ask: ToolAsk;
|
|
18
|
+
};
|
|
19
|
+
export interface BrokerDeps {
|
|
20
|
+
gate: (ask: ToolAsk) => Promise<GateOutcome>;
|
|
21
|
+
askHuman: (q: HumanQuestion) => Promise<HumanAnswer>;
|
|
22
|
+
onGate: (outcome: GateOutcome, ask: ToolAsk) => void;
|
|
23
|
+
/** True when the repo's git config names a program. Absent means unknown, and git then asks. */
|
|
24
|
+
gitRunsPrograms?: () => Promise<boolean>;
|
|
25
|
+
}
|
|
26
|
+
export declare function createBroker(deps: BrokerDeps): (ask: ToolAsk) => Promise<BrokerVerdict>;
|
|
27
|
+
//# sourceMappingURL=permission-broker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission-broker.d.ts","sourceRoot":"","sources":["../../../src/lib/agent/permission-broker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAgB,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAEjG;;;;;GAKG;AAEH,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC,WAAW,EAAE;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE,CAAC,CAAC;IAAC,GAAG,EAAE,OAAO,CAAA;CAAE,GAC9E;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,OAAO,CAAA;CAAE,CAAC;AAEvD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAC7C,QAAQ,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IACrD,MAAM,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IACrD,gGAAgG;IAChG,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1C;AAkBD,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,aAAa,CAAC,CAmCvF"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { commandUsesGit, isReadOnlyCommand } from './read-only-command.js';
|
|
2
|
+
/** Only an explicit yes allows. An answer this code does not know (a future key, a cancel) is a stop. */
|
|
3
|
+
const allows = (a) => a === 'allow' || a === 'allow-session';
|
|
4
|
+
function decisionLine(d) {
|
|
5
|
+
return [`- ${d.title}`, d.why ? ` - ${d.why}` : '', d.url ? ` (${d.url})` : ''].join('');
|
|
6
|
+
}
|
|
7
|
+
function stopMessage(outcome) {
|
|
8
|
+
const lead = {
|
|
9
|
+
conflict: 'Align: the person stopped this edit. It conflicts with a recorded decision:',
|
|
10
|
+
'rewrites-decided-lines': 'Align: the person stopped this edit. It changes lines written by a recorded decision:',
|
|
11
|
+
unchecked: 'Align: the person stopped this edit. Align could not read it to check it against the decisions.',
|
|
12
|
+
}[outcome.reason];
|
|
13
|
+
return [lead, ...outcome.cards.map(decisionLine), 'Do not retry this edit. Ask the person how they want to proceed.'].join('\n');
|
|
14
|
+
}
|
|
15
|
+
export function createBroker(deps) {
|
|
16
|
+
// "Yes for this session" remembers the EXACT command, or the tool by name. A prefix match let
|
|
17
|
+
// an allowed `git status` approve `git status; rm -rf ~` (security review).
|
|
18
|
+
const sessionAllowed = new Set();
|
|
19
|
+
return async (ask) => {
|
|
20
|
+
if (ask.kind === 'read')
|
|
21
|
+
return { allow: true };
|
|
22
|
+
if (ask.kind === 'edit') {
|
|
23
|
+
const outcome = await deps.gate(ask);
|
|
24
|
+
deps.onGate(outcome, ask);
|
|
25
|
+
if (outcome.kind === 'allow')
|
|
26
|
+
return { allow: true };
|
|
27
|
+
const answer = await deps.askHuman({ kind: 'gate', outcome, ask });
|
|
28
|
+
return allows(answer) ? { allow: true } : { allow: false, message: stopMessage(outcome) };
|
|
29
|
+
}
|
|
30
|
+
// A command that can only read passes; anything chained, redirected or unknown still asks.
|
|
31
|
+
if (ask.kind === 'command' && isReadOnlyCommand(ask.command ?? '')) {
|
|
32
|
+
// git reads can still run a program the repo's config names, so those pass only once that is ruled out.
|
|
33
|
+
if (!commandUsesGit(ask.command ?? ''))
|
|
34
|
+
return { allow: true };
|
|
35
|
+
if (deps.gitRunsPrograms && !(await deps.gitRunsPrograms()))
|
|
36
|
+
return { allow: true };
|
|
37
|
+
}
|
|
38
|
+
// A command, or a tool this code does not know (WebFetch, an MCP tool, a subagent): a person decides.
|
|
39
|
+
// An unknown tool is approved for its input, not its name: a yes to one URL is not a yes to all.
|
|
40
|
+
// The question shows the WHOLE input: approval covers all of it, so none may hide past a cut.
|
|
41
|
+
const label = ask.kind === 'command' ? (ask.command ?? '') : [ask.tool, ask.detail].filter(Boolean).join(' ');
|
|
42
|
+
const key = ask.kind === 'command' ? `command:${label}` : `other:${ask.tool}:${ask.detail ?? ''}`;
|
|
43
|
+
if (sessionAllowed.has(key))
|
|
44
|
+
return { allow: true };
|
|
45
|
+
const answer = await deps.askHuman({ kind: 'command', command: label, ask });
|
|
46
|
+
if (!allows(answer)) {
|
|
47
|
+
return { allow: false, message: ask.kind === 'command'
|
|
48
|
+
? `Align: the person declined to run \`${label}\`. Ask them before trying another way.`
|
|
49
|
+
: `Align: the person declined the ${ask.tool} tool. Ask them before trying another way.` };
|
|
50
|
+
}
|
|
51
|
+
if (answer === 'allow-session')
|
|
52
|
+
sessionAllowed.add(key);
|
|
53
|
+
return { allow: true };
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=permission-broker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission-broker.js","sourceRoot":"","sources":["../../../src/lib/agent/permission-broker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which shell commands the broker lets through without asking. The rule is an allowlist over a
|
|
3
|
+
* parse that fails toward asking: anything this file cannot positively read as read-only is not.
|
|
4
|
+
*
|
|
5
|
+
* A command is one or more segments joined by `;`, `&&`, `||` or `|`. EVERY segment must be a
|
|
6
|
+
* known read-only command, so an allowed `git status` cannot approve what follows it. Characters
|
|
7
|
+
* that run or write something else (substitution, redirects, background jobs, newlines, escapes)
|
|
8
|
+
* reject the whole line. A quoted `;` splits wrongly, and that only ever produces a segment that
|
|
9
|
+
* fails the allowlist, so the mistake costs a question and never a pass.
|
|
10
|
+
*/
|
|
11
|
+
/** True when any segment of the line runs git, which can run programs the repo's config names. */
|
|
12
|
+
export declare function commandUsesGit(command: string): boolean;
|
|
13
|
+
/** True only when every segment of the line is a known read-only command. */
|
|
14
|
+
export declare function isReadOnlyCommand(command: string): boolean;
|
|
15
|
+
//# sourceMappingURL=read-only-command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-only-command.d.ts","sourceRoot":"","sources":["../../../src/lib/agent/read-only-command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAuEH,kGAAkG;AAClG,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAGvD;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAI1D"}
|