@memberjunction/ai-agent-harness 0.0.0 → 6.1.0-edge.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -0
- package/README.md +192 -27
- package/dist/HarnessAgentBase.d.ts +261 -0
- package/dist/HarnessAgentBase.d.ts.map +1 -0
- package/dist/HarnessAgentBase.js +822 -0
- package/dist/HarnessAgentBase.js.map +1 -0
- package/dist/HarnessAgentType.d.ts +39 -0
- package/dist/HarnessAgentType.d.ts.map +1 -0
- package/dist/HarnessAgentType.js +50 -0
- package/dist/HarnessAgentType.js.map +1 -0
- package/dist/adapters/BaseCliHarnessAdapter.d.ts +93 -0
- package/dist/adapters/BaseCliHarnessAdapter.d.ts.map +1 -0
- package/dist/adapters/BaseCliHarnessAdapter.js +184 -0
- package/dist/adapters/BaseCliHarnessAdapter.js.map +1 -0
- package/dist/adapters/BaseHarnessAdapter.d.ts +114 -0
- package/dist/adapters/BaseHarnessAdapter.d.ts.map +1 -0
- package/dist/adapters/BaseHarnessAdapter.js +86 -0
- package/dist/adapters/BaseHarnessAdapter.js.map +1 -0
- package/dist/adapters/ClaudeCodeCliAdapter.d.ts +104 -0
- package/dist/adapters/ClaudeCodeCliAdapter.d.ts.map +1 -0
- package/dist/adapters/ClaudeCodeCliAdapter.js +268 -0
- package/dist/adapters/ClaudeCodeCliAdapter.js.map +1 -0
- package/dist/adapters/CodexAdapter.d.ts +27 -0
- package/dist/adapters/CodexAdapter.d.ts.map +1 -0
- package/dist/adapters/CodexAdapter.js +117 -0
- package/dist/adapters/CodexAdapter.js.map +1 -0
- package/dist/adapters/GeminiCliAdapter.d.ts +25 -0
- package/dist/adapters/GeminiCliAdapter.d.ts.map +1 -0
- package/dist/adapters/GeminiCliAdapter.js +98 -0
- package/dist/adapters/GeminiCliAdapter.js.map +1 -0
- package/dist/adapters/OpenCodeAdapter.d.ts +23 -0
- package/dist/adapters/OpenCodeAdapter.d.ts.map +1 -0
- package/dist/adapters/OpenCodeAdapter.js +104 -0
- package/dist/adapters/OpenCodeAdapter.js.map +1 -0
- package/dist/adapters/PiAdapter.d.ts +73 -0
- package/dist/adapters/PiAdapter.d.ts.map +1 -0
- package/dist/adapters/PiAdapter.js +237 -0
- package/dist/adapters/PiAdapter.js.map +1 -0
- package/dist/adapters/StdioJsonAdapter.d.ts +43 -0
- package/dist/adapters/StdioJsonAdapter.d.ts.map +1 -0
- package/dist/adapters/StdioJsonAdapter.js +109 -0
- package/dist/adapters/StdioJsonAdapter.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/sandbox/ChildProcessExecutor.d.ts +41 -0
- package/dist/sandbox/ChildProcessExecutor.d.ts.map +1 -0
- package/dist/sandbox/ChildProcessExecutor.js +86 -0
- package/dist/sandbox/ChildProcessExecutor.js.map +1 -0
- package/dist/sandbox/DockerSandboxProvider.d.ts +64 -0
- package/dist/sandbox/DockerSandboxProvider.d.ts.map +1 -0
- package/dist/sandbox/DockerSandboxProvider.js +176 -0
- package/dist/sandbox/DockerSandboxProvider.js.map +1 -0
- package/dist/sandbox/ISandboxProvider.d.ts +71 -0
- package/dist/sandbox/ISandboxProvider.d.ts.map +1 -0
- package/dist/sandbox/ISandboxProvider.js +2 -0
- package/dist/sandbox/ISandboxProvider.js.map +1 -0
- package/dist/sandbox/LocalDirectorySandboxProvider.d.ts +37 -0
- package/dist/sandbox/LocalDirectorySandboxProvider.d.ts.map +1 -0
- package/dist/sandbox/LocalDirectorySandboxProvider.js +75 -0
- package/dist/sandbox/LocalDirectorySandboxProvider.js.map +1 -0
- package/dist/sandbox/SandboxExecutor.d.ts +47 -0
- package/dist/sandbox/SandboxExecutor.d.ts.map +1 -0
- package/dist/sandbox/SandboxExecutor.js +2 -0
- package/dist/sandbox/SandboxExecutor.js.map +1 -0
- package/dist/types.d.ts +180 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +35 -8
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { BaseCliHarnessAdapter, HarnessCliRawEvent } from './BaseCliHarnessAdapter.js';
|
|
2
|
+
import { HarnessCapabilities, HarnessPermissionPolicy, HarnessTurnEvent } from '../types.js';
|
|
3
|
+
import { HarnessProcess } from '../sandbox/SandboxExecutor.js';
|
|
4
|
+
/**
|
|
5
|
+
* Drives Claude Code through its headless CLI. This is the ONLY Claude Code adapter.
|
|
6
|
+
*
|
|
7
|
+
* ## Why not the Claude Agent SDK
|
|
8
|
+
*
|
|
9
|
+
* An SDK-based adapter was built and then deliberately removed. The SDK offers a programmatic
|
|
10
|
+
* permission callback (`canUseTool`) and in-process MCP tools, both genuinely useful — but it runs
|
|
11
|
+
* IN-PROCESS in Node, and no sandbox executor can place an in-process library inside a container.
|
|
12
|
+
*
|
|
13
|
+
* That trade does not survive scrutiny: it buys better permission *hooks* at the cost of any
|
|
14
|
+
* process *isolation*, for a feature whose entire purpose is executing an autonomous agent's shell
|
|
15
|
+
* commands. Worse, an SDK-backed agent configured with `provider: 'docker'` would run outside its
|
|
16
|
+
* sandbox while the config claimed otherwise — the same false-containment failure this design
|
|
17
|
+
* refuses elsewhere. And the asymmetry is decisive: the CLI's missing permission hook is fixable
|
|
18
|
+
* (an MCP permission-prompt tool), while the SDK's missing containment is not.
|
|
19
|
+
*
|
|
20
|
+
* Two incidental findings that removed the remaining arguments for it: the SDK's typing advantage
|
|
21
|
+
* largely evaporated in practice (its message union is too broad to narrow against, so the adapter
|
|
22
|
+
* read fields structurally anyway — exactly what the CLI adapter does), and the SDK is not
|
|
23
|
+
* dependency-free — it ships a ~259 MB platform-specific `claude` binary. It wraps the same program
|
|
24
|
+
* this adapter invokes.
|
|
25
|
+
*
|
|
26
|
+
* So there is one Claude path, and it works in all three deployments: local spawn, local container,
|
|
27
|
+
* cloud container.
|
|
28
|
+
*/
|
|
29
|
+
export declare class ClaudeCodeCliAdapter extends BaseCliHarnessAdapter {
|
|
30
|
+
private executable;
|
|
31
|
+
private systemPrompt;
|
|
32
|
+
private didResume;
|
|
33
|
+
private permissionArgs;
|
|
34
|
+
/**
|
|
35
|
+
* Maps MJ's posture onto Claude Code's permission vocabulary.
|
|
36
|
+
*
|
|
37
|
+
* Conservative at both ends, deliberately:
|
|
38
|
+
*
|
|
39
|
+
* - `strict` adds NOTHING, leaving Claude Code's default prompting in place. Headless, those
|
|
40
|
+
* prompts have nowhere to go and every call denies — the correct outcome for a posture whose
|
|
41
|
+
* contract is "no mutation without a human" when MJ has no approval channel yet. Observably
|
|
42
|
+
* useless beats quietly permissive.
|
|
43
|
+
* - `auto` uses `acceptEdits`, NOT `bypassPermissions`. Edits inside the workspace proceed while
|
|
44
|
+
* genuinely dangerous operations still gate; using bypass here would make `auto` and
|
|
45
|
+
* `dangerous` the same setting under two names.
|
|
46
|
+
* - `dangerous` passes `--dangerously-skip-permissions`, which Claude Code's own help restricts
|
|
47
|
+
* to sandboxes with no internet access. Pairing it with the LOCAL provider is a
|
|
48
|
+
* misconfiguration — that provider does not contain the process at all.
|
|
49
|
+
*
|
|
50
|
+
* Allow/deny patterns pass through in Claude Code's own syntax, deny applied last so it wins:
|
|
51
|
+
* an overlapping policy must fail closed.
|
|
52
|
+
*
|
|
53
|
+
* ## Bash patterns are PREFIX-LITERAL — they are hygiene, not a boundary
|
|
54
|
+
*
|
|
55
|
+
* `Bash(git commit:*)` matches only commands that *begin* with `git commit`. Proven live: a
|
|
56
|
+
* `Bash(git:*)` allow paired with a `Bash(git commit:*)` deny let `git -C <path> commit` through,
|
|
57
|
+
* because any flag before the subcommand defeats the prefix. The run failed only because nothing
|
|
58
|
+
* happened to be staged.
|
|
59
|
+
*
|
|
60
|
+
* The consequence is a rule, not a caveat: **do not use a broad `Bash(<tool>:*)` allow and try to
|
|
61
|
+
* carve dangerous subcommands back out with denies.** Deny whole tool names (`Bash` — an exact
|
|
62
|
+
* name match, no prefix involved) or allow only fully-specified commands. Real containment comes
|
|
63
|
+
* from the sandbox provider; with the LOCAL provider there is none, so the policy is all there is.
|
|
64
|
+
*/
|
|
65
|
+
ApplyPermissionPolicy(policy: HarnessPermissionPolicy): void;
|
|
66
|
+
/** @inheritdoc */
|
|
67
|
+
get DidResumeSession(): boolean;
|
|
68
|
+
/** @inheritdoc */
|
|
69
|
+
SetSystemPrompt(systemPrompt: string): void;
|
|
70
|
+
protected get ExecutablePath(): string;
|
|
71
|
+
/** @inheritdoc */
|
|
72
|
+
get Capabilities(): HarnessCapabilities;
|
|
73
|
+
protected BuildTurnArgs(input: string, isFirstTurn: boolean): string[];
|
|
74
|
+
protected MapEvent(raw: HarnessCliRawEvent): HarnessTurnEvent | null;
|
|
75
|
+
/**
|
|
76
|
+
* Handles a `result` line, which carries BOTH the turn's usage and its outcome.
|
|
77
|
+
*
|
|
78
|
+
* MapEvent is one-in-one-out, so usage is returned now and the outcome is parked in
|
|
79
|
+
* {@link pendingTerminal} for {@link readEvents} to flush immediately after. Emitting usage
|
|
80
|
+
* first matters: a turn that failed still spent tokens getting there, and the run's cost
|
|
81
|
+
* accounting should see them either way.
|
|
82
|
+
*/
|
|
83
|
+
private buildResultEvents;
|
|
84
|
+
/** Turns a `result` message into either a completed turn or a failed one. */
|
|
85
|
+
private buildTerminalEvent;
|
|
86
|
+
/** Concatenates the text blocks of an assistant message. */
|
|
87
|
+
private extractAssistantText;
|
|
88
|
+
/** Terminal event held back while its usage event is emitted first. */
|
|
89
|
+
private pendingTerminal;
|
|
90
|
+
/** The model Claude reported using on this turn. */
|
|
91
|
+
private reportedModel;
|
|
92
|
+
/** @inheritdoc */
|
|
93
|
+
get ReportedModel(): string | undefined;
|
|
94
|
+
/**
|
|
95
|
+
* Flushes the terminal event held back by {@link buildResultEvents}.
|
|
96
|
+
*
|
|
97
|
+
* Overridden so a single `result` line can yield both usage and outcome while MapEvent keeps
|
|
98
|
+
* its simple one-in-one-out shape.
|
|
99
|
+
*/
|
|
100
|
+
protected readEvents(proc: HarnessProcess): AsyncIterable<HarnessTurnEvent>;
|
|
101
|
+
/** Points the adapter at a specific binary, from `AIAgentHarness.ExecutablePath`. */
|
|
102
|
+
SetExecutable(path: string): void;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=ClaudeCodeCliAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClaudeCodeCliAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/ClaudeCodeCliAdapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEvF,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBACa,oBAAqB,SAAQ,qBAAqB;IAC3D,OAAO,CAAC,UAAU,CAAY;IAC9B,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,cAAc,CAAgB;IAEtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACa,qBAAqB,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI;IAsB5E,kBAAkB;IAClB,IAAoB,gBAAgB,IAAI,OAAO,CAE9C;IAED,kBAAkB;IACF,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAI3D,SAAS,KAAK,cAAc,IAAI,MAAM,CAErC;IAED,kBAAkB;IAClB,IAAW,YAAY,IAAI,mBAAmB,CAwB7C;IAED,SAAS,CAAC,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,MAAM,EAAE;IAkCtE,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,kBAAkB,GAAG,gBAAgB,GAAG,IAAI;IAiCpE;;;;;;;OAOG;IACH,OAAO,CAAC,iBAAiB;IAUzB,6EAA6E;IAC7E,OAAO,CAAC,kBAAkB;IAQ1B,4DAA4D;IAC5D,OAAO,CAAC,oBAAoB;IAiB5B,uEAAuE;IACvE,OAAO,CAAC,eAAe,CAAiC;IAExD,oDAAoD;IACpD,OAAO,CAAC,aAAa,CAAqB;IAE1C,kBAAkB;IAClB,IAAoB,aAAa,IAAI,MAAM,GAAG,SAAS,CAEtD;IAED;;;;;OAKG;cACuB,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,aAAa,CAAC,gBAAgB,CAAC;IAW3F,qFAAqF;IAC9E,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;CAG3C"}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
8
|
+
import { BaseCliHarnessAdapter } from './BaseCliHarnessAdapter.js';
|
|
9
|
+
import { BaseHarnessAdapter } from './BaseHarnessAdapter.js';
|
|
10
|
+
/**
|
|
11
|
+
* Drives Claude Code through its headless CLI. This is the ONLY Claude Code adapter.
|
|
12
|
+
*
|
|
13
|
+
* ## Why not the Claude Agent SDK
|
|
14
|
+
*
|
|
15
|
+
* An SDK-based adapter was built and then deliberately removed. The SDK offers a programmatic
|
|
16
|
+
* permission callback (`canUseTool`) and in-process MCP tools, both genuinely useful — but it runs
|
|
17
|
+
* IN-PROCESS in Node, and no sandbox executor can place an in-process library inside a container.
|
|
18
|
+
*
|
|
19
|
+
* That trade does not survive scrutiny: it buys better permission *hooks* at the cost of any
|
|
20
|
+
* process *isolation*, for a feature whose entire purpose is executing an autonomous agent's shell
|
|
21
|
+
* commands. Worse, an SDK-backed agent configured with `provider: 'docker'` would run outside its
|
|
22
|
+
* sandbox while the config claimed otherwise — the same false-containment failure this design
|
|
23
|
+
* refuses elsewhere. And the asymmetry is decisive: the CLI's missing permission hook is fixable
|
|
24
|
+
* (an MCP permission-prompt tool), while the SDK's missing containment is not.
|
|
25
|
+
*
|
|
26
|
+
* Two incidental findings that removed the remaining arguments for it: the SDK's typing advantage
|
|
27
|
+
* largely evaporated in practice (its message union is too broad to narrow against, so the adapter
|
|
28
|
+
* read fields structurally anyway — exactly what the CLI adapter does), and the SDK is not
|
|
29
|
+
* dependency-free — it ships a ~259 MB platform-specific `claude` binary. It wraps the same program
|
|
30
|
+
* this adapter invokes.
|
|
31
|
+
*
|
|
32
|
+
* So there is one Claude path, and it works in all three deployments: local spawn, local container,
|
|
33
|
+
* cloud container.
|
|
34
|
+
*/
|
|
35
|
+
let ClaudeCodeCliAdapter = class ClaudeCodeCliAdapter extends BaseCliHarnessAdapter {
|
|
36
|
+
constructor() {
|
|
37
|
+
super(...arguments);
|
|
38
|
+
this.executable = 'claude';
|
|
39
|
+
this.didResume = false;
|
|
40
|
+
this.permissionArgs = [];
|
|
41
|
+
/** Terminal event held back while its usage event is emitted first. */
|
|
42
|
+
this.pendingTerminal = null;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Maps MJ's posture onto Claude Code's permission vocabulary.
|
|
46
|
+
*
|
|
47
|
+
* Conservative at both ends, deliberately:
|
|
48
|
+
*
|
|
49
|
+
* - `strict` adds NOTHING, leaving Claude Code's default prompting in place. Headless, those
|
|
50
|
+
* prompts have nowhere to go and every call denies — the correct outcome for a posture whose
|
|
51
|
+
* contract is "no mutation without a human" when MJ has no approval channel yet. Observably
|
|
52
|
+
* useless beats quietly permissive.
|
|
53
|
+
* - `auto` uses `acceptEdits`, NOT `bypassPermissions`. Edits inside the workspace proceed while
|
|
54
|
+
* genuinely dangerous operations still gate; using bypass here would make `auto` and
|
|
55
|
+
* `dangerous` the same setting under two names.
|
|
56
|
+
* - `dangerous` passes `--dangerously-skip-permissions`, which Claude Code's own help restricts
|
|
57
|
+
* to sandboxes with no internet access. Pairing it with the LOCAL provider is a
|
|
58
|
+
* misconfiguration — that provider does not contain the process at all.
|
|
59
|
+
*
|
|
60
|
+
* Allow/deny patterns pass through in Claude Code's own syntax, deny applied last so it wins:
|
|
61
|
+
* an overlapping policy must fail closed.
|
|
62
|
+
*
|
|
63
|
+
* ## Bash patterns are PREFIX-LITERAL — they are hygiene, not a boundary
|
|
64
|
+
*
|
|
65
|
+
* `Bash(git commit:*)` matches only commands that *begin* with `git commit`. Proven live: a
|
|
66
|
+
* `Bash(git:*)` allow paired with a `Bash(git commit:*)` deny let `git -C <path> commit` through,
|
|
67
|
+
* because any flag before the subcommand defeats the prefix. The run failed only because nothing
|
|
68
|
+
* happened to be staged.
|
|
69
|
+
*
|
|
70
|
+
* The consequence is a rule, not a caveat: **do not use a broad `Bash(<tool>:*)` allow and try to
|
|
71
|
+
* carve dangerous subcommands back out with denies.** Deny whole tool names (`Bash` — an exact
|
|
72
|
+
* name match, no prefix involved) or allow only fully-specified commands. Real containment comes
|
|
73
|
+
* from the sandbox provider; with the LOCAL provider there is none, so the policy is all there is.
|
|
74
|
+
*/
|
|
75
|
+
ApplyPermissionPolicy(policy) {
|
|
76
|
+
const args = [];
|
|
77
|
+
switch (policy.Posture) {
|
|
78
|
+
case 'auto':
|
|
79
|
+
args.push('--permission-mode', 'acceptEdits');
|
|
80
|
+
break;
|
|
81
|
+
case 'dangerous':
|
|
82
|
+
args.push('--dangerously-skip-permissions');
|
|
83
|
+
break;
|
|
84
|
+
case 'strict':
|
|
85
|
+
default:
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
if (policy.AllowedTools?.length) {
|
|
89
|
+
args.push('--allowedTools', ...policy.AllowedTools);
|
|
90
|
+
}
|
|
91
|
+
if (policy.DisallowedTools?.length) {
|
|
92
|
+
args.push('--disallowedTools', ...policy.DisallowedTools);
|
|
93
|
+
}
|
|
94
|
+
this.permissionArgs = args;
|
|
95
|
+
}
|
|
96
|
+
/** @inheritdoc */
|
|
97
|
+
get DidResumeSession() {
|
|
98
|
+
return this.didResume;
|
|
99
|
+
}
|
|
100
|
+
/** @inheritdoc */
|
|
101
|
+
SetSystemPrompt(systemPrompt) {
|
|
102
|
+
this.systemPrompt = systemPrompt;
|
|
103
|
+
}
|
|
104
|
+
get ExecutablePath() {
|
|
105
|
+
return this.executable;
|
|
106
|
+
}
|
|
107
|
+
/** @inheritdoc */
|
|
108
|
+
get Capabilities() {
|
|
109
|
+
return {
|
|
110
|
+
SessionResume: true,
|
|
111
|
+
// Killing the process ends the turn immediately — the executor owns the process handle.
|
|
112
|
+
MidTurnCancellation: true,
|
|
113
|
+
// FALSE, deliberately. `--output-format stream-json` structures the TRANSPORT, not the
|
|
114
|
+
// model's content — nothing constrains what Claude actually writes inside a turn. Claiming
|
|
115
|
+
// true here told the runtime it need not compensate, and the harness spent five turns
|
|
116
|
+
// inventing step names (`complete`, `respond`, `undefined`) before BaseAgent's retry
|
|
117
|
+
// feedback taught it the Loop vocabulary. Same distinction PiAdapter already documents.
|
|
118
|
+
StructuredOutput: false,
|
|
119
|
+
UsageReporting: true,
|
|
120
|
+
// ApplyPermissionPolicy below translates posture + allow/deny into real launch flags.
|
|
121
|
+
PermissionPolicy: true,
|
|
122
|
+
// Claude Code CAN intercept permissions, but only through an MCP permission-prompt tool
|
|
123
|
+
// that MJ has not stood up yet. Reported false until that exists: claiming interception
|
|
124
|
+
// the adapter does not implement would let mutating operations through unreviewed while
|
|
125
|
+
// the strict posture believed it was gating them. This is the one capability the removed
|
|
126
|
+
// SDK adapter had and this one does not, and it is a prerequisite for `strict`.
|
|
127
|
+
PermissionHooks: false,
|
|
128
|
+
McpClient: true,
|
|
129
|
+
WorkspaceScoping: true,
|
|
130
|
+
ModelSelection: true,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
BuildTurnArgs(input, isFirstTurn) {
|
|
134
|
+
const args = ['-p', input, '--output-format', 'stream-json', '--verbose'];
|
|
135
|
+
// Claude Code persists sessions to its own on-disk store, so resuming needs no live process
|
|
136
|
+
// — which is why this works at all despite the process dying after every turn. If the store
|
|
137
|
+
// has pruned the session the CLI starts fresh rather than failing, so an optimistic resume
|
|
138
|
+
// degrades to a cold session instead of breaking the run.
|
|
139
|
+
if (isFirstTurn && this.config?.ResumeSessionId) {
|
|
140
|
+
this.sessionId = this.config.ResumeSessionId;
|
|
141
|
+
this.didResume = true;
|
|
142
|
+
}
|
|
143
|
+
// APPEND rather than replace: Claude Code's own system prompt carries its tool definitions
|
|
144
|
+
// and sandbox conventions, and replacing it would break the harness to enforce our envelope.
|
|
145
|
+
// Appending puts MJ's contract at system level, where it outranks the conversational habit
|
|
146
|
+
// that was costing a retry on turn one.
|
|
147
|
+
if (isFirstTurn && this.systemPrompt) {
|
|
148
|
+
args.push('--append-system-prompt', this.systemPrompt);
|
|
149
|
+
}
|
|
150
|
+
// Pass --resume when continuing WITHIN a run (turn 2+) OR when continuing a PRIOR run's
|
|
151
|
+
// session on turn 1. Gating on `!isFirstTurn` alone silently defeated cross-run resume: the
|
|
152
|
+
// session id was looked up and assigned, the log said "Resuming...", and the flag was never
|
|
153
|
+
// actually passed — so Claude started cold and MapEvent overwrote the id with the new one.
|
|
154
|
+
// The run then recorded a different session than the one it claimed to resume.
|
|
155
|
+
if (this.sessionId && (!isFirstTurn || this.didResume)) {
|
|
156
|
+
args.push('--resume', this.sessionId);
|
|
157
|
+
}
|
|
158
|
+
if (this.config?.Model) {
|
|
159
|
+
args.push('--model', this.config.Model);
|
|
160
|
+
}
|
|
161
|
+
// Per-invocation, not per-session: the process is new every turn, so a policy applied only
|
|
162
|
+
// at session start would silently lapse from turn 2 onward.
|
|
163
|
+
args.push(...this.permissionArgs);
|
|
164
|
+
return args;
|
|
165
|
+
}
|
|
166
|
+
MapEvent(raw) {
|
|
167
|
+
const session = this.readString(raw, 'session_id');
|
|
168
|
+
if (session) {
|
|
169
|
+
this.sessionId = session;
|
|
170
|
+
}
|
|
171
|
+
switch (this.readString(raw, 'type')) {
|
|
172
|
+
case 'assistant': {
|
|
173
|
+
// Claude reports the model it actually used on each assistant message. Capture it so
|
|
174
|
+
// accounting reflects reality rather than the model we assumed.
|
|
175
|
+
const message = this.readObject(raw, 'message');
|
|
176
|
+
const model = message ? this.readString(message, 'model') : undefined;
|
|
177
|
+
if (model) {
|
|
178
|
+
this.reportedModel = model;
|
|
179
|
+
}
|
|
180
|
+
const text = this.extractAssistantText(raw);
|
|
181
|
+
return text ? { Type: 'assistant-text', Text: text } : null;
|
|
182
|
+
}
|
|
183
|
+
case 'result': {
|
|
184
|
+
const usage = this.readObject(raw, 'usage');
|
|
185
|
+
if (usage) {
|
|
186
|
+
// Usage arrives on the same message as the result. It is emitted as its own
|
|
187
|
+
// event so the accumulation loop stays uniform across every adapter, and so a
|
|
188
|
+
// turn that errors still reports what it spent getting there.
|
|
189
|
+
return this.buildResultEvents(raw, usage);
|
|
190
|
+
}
|
|
191
|
+
return this.buildTerminalEvent(raw);
|
|
192
|
+
}
|
|
193
|
+
default:
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Handles a `result` line, which carries BOTH the turn's usage and its outcome.
|
|
199
|
+
*
|
|
200
|
+
* MapEvent is one-in-one-out, so usage is returned now and the outcome is parked in
|
|
201
|
+
* {@link pendingTerminal} for {@link readEvents} to flush immediately after. Emitting usage
|
|
202
|
+
* first matters: a turn that failed still spent tokens getting there, and the run's cost
|
|
203
|
+
* accounting should see them either way.
|
|
204
|
+
*/
|
|
205
|
+
buildResultEvents(raw, usage) {
|
|
206
|
+
this.pendingTerminal = this.buildTerminalEvent(raw);
|
|
207
|
+
return {
|
|
208
|
+
Type: 'usage',
|
|
209
|
+
InputTokens: this.readNumber(usage, 'input_tokens') ?? 0,
|
|
210
|
+
OutputTokens: this.readNumber(usage, 'output_tokens') ?? 0,
|
|
211
|
+
CostUsd: this.readNumber(raw, 'total_cost_usd'),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/** Turns a `result` message into either a completed turn or a failed one. */
|
|
215
|
+
buildTerminalEvent(raw) {
|
|
216
|
+
const text = this.readString(raw, 'result') ?? '';
|
|
217
|
+
const isError = raw['is_error'] === true;
|
|
218
|
+
return isError
|
|
219
|
+
? { Type: 'session-error', Error: text || 'Claude Code reported an error' }
|
|
220
|
+
: { Type: 'turn-complete', RawText: text };
|
|
221
|
+
}
|
|
222
|
+
/** Concatenates the text blocks of an assistant message. */
|
|
223
|
+
extractAssistantText(raw) {
|
|
224
|
+
const message = this.readObject(raw, 'message');
|
|
225
|
+
const content = message?.['content'];
|
|
226
|
+
if (!Array.isArray(content)) {
|
|
227
|
+
return '';
|
|
228
|
+
}
|
|
229
|
+
return content
|
|
230
|
+
.map((block) => {
|
|
231
|
+
if (block === null || typeof block !== 'object') {
|
|
232
|
+
return '';
|
|
233
|
+
}
|
|
234
|
+
const asRecord = block;
|
|
235
|
+
return asRecord['type'] === 'text' ? (this.readString(asRecord, 'text') ?? '') : '';
|
|
236
|
+
})
|
|
237
|
+
.join('');
|
|
238
|
+
}
|
|
239
|
+
/** @inheritdoc */
|
|
240
|
+
get ReportedModel() {
|
|
241
|
+
return this.reportedModel;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Flushes the terminal event held back by {@link buildResultEvents}.
|
|
245
|
+
*
|
|
246
|
+
* Overridden so a single `result` line can yield both usage and outcome while MapEvent keeps
|
|
247
|
+
* its simple one-in-one-out shape.
|
|
248
|
+
*/
|
|
249
|
+
async *readEvents(proc) {
|
|
250
|
+
for await (const event of super.readEvents(proc)) {
|
|
251
|
+
yield event;
|
|
252
|
+
if (this.pendingTerminal) {
|
|
253
|
+
const terminal = this.pendingTerminal;
|
|
254
|
+
this.pendingTerminal = null;
|
|
255
|
+
yield terminal;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/** Points the adapter at a specific binary, from `AIAgentHarness.ExecutablePath`. */
|
|
260
|
+
SetExecutable(path) {
|
|
261
|
+
this.executable = path;
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
ClaudeCodeCliAdapter = __decorate([
|
|
265
|
+
RegisterClass(BaseHarnessAdapter, 'ClaudeCodeCliAdapter')
|
|
266
|
+
], ClaudeCodeCliAdapter);
|
|
267
|
+
export { ClaudeCodeCliAdapter };
|
|
268
|
+
//# sourceMappingURL=ClaudeCodeCliAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClaudeCodeCliAdapter.js","sourceRoot":"","sources":["../../src/adapters/ClaudeCodeCliAdapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { BaseCliHarnessAdapter, HarnessCliRawEvent } from './BaseCliHarnessAdapter.js';
|
|
2
|
+
import { HarnessCapabilities, HarnessTurnEvent } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Drives OpenAI's Codex CLI (`@openai/codex`).
|
|
5
|
+
*
|
|
6
|
+
* Codex ships as a CLI only — the npm package exposes a `bin` and no library entry point — so this
|
|
7
|
+
* goes through {@link BaseCliHarnessAdapter} rather than an SDK.
|
|
8
|
+
*
|
|
9
|
+
* ## About the argv defaults
|
|
10
|
+
*
|
|
11
|
+
* `codex exec --json` is the documented headless form, but coding-agent CLIs move fast and flag
|
|
12
|
+
* names drift between minor versions. Everything version-sensitive is confined to
|
|
13
|
+
* {@link BuildTurnArgs} and the event names in {@link MapEvent}, so adapting to a new Codex release
|
|
14
|
+
* is a change in one small place rather than a redesign — and `AIAgentHarness.ExecutablePath` lets a
|
|
15
|
+
* deployment pin a specific binary without touching code at all.
|
|
16
|
+
*/
|
|
17
|
+
export declare class CodexAdapter extends BaseCliHarnessAdapter {
|
|
18
|
+
protected get ExecutablePath(): string;
|
|
19
|
+
private executable;
|
|
20
|
+
/** @inheritdoc */
|
|
21
|
+
get Capabilities(): HarnessCapabilities;
|
|
22
|
+
protected BuildTurnArgs(input: string, isFirstTurn: boolean): string[];
|
|
23
|
+
protected MapEvent(raw: HarnessCliRawEvent): HarnessTurnEvent | null;
|
|
24
|
+
/** Points the adapter at a specific binary, from `AIAgentHarness.ExecutablePath`. */
|
|
25
|
+
SetExecutable(path: string): void;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=CodexAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CodexAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/CodexAdapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEvF,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpE;;;;;;;;;;;;;GAaG;AACH,qBACa,YAAa,SAAQ,qBAAqB;IACnD,SAAS,KAAK,cAAc,IAAI,MAAM,CAErC;IAED,OAAO,CAAC,UAAU,CAAW;IAE7B,kBAAkB;IAClB,IAAW,YAAY,IAAI,mBAAmB,CAoB7C;IAED,SAAS,CAAC,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,MAAM,EAAE;IAYtE,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,kBAAkB,GAAG,gBAAgB,GAAG,IAAI;IA8CpE,qFAAqF;IAC9E,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;CAG3C"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
8
|
+
import { BaseCliHarnessAdapter } from './BaseCliHarnessAdapter.js';
|
|
9
|
+
import { BaseHarnessAdapter } from './BaseHarnessAdapter.js';
|
|
10
|
+
/**
|
|
11
|
+
* Drives OpenAI's Codex CLI (`@openai/codex`).
|
|
12
|
+
*
|
|
13
|
+
* Codex ships as a CLI only — the npm package exposes a `bin` and no library entry point — so this
|
|
14
|
+
* goes through {@link BaseCliHarnessAdapter} rather than an SDK.
|
|
15
|
+
*
|
|
16
|
+
* ## About the argv defaults
|
|
17
|
+
*
|
|
18
|
+
* `codex exec --json` is the documented headless form, but coding-agent CLIs move fast and flag
|
|
19
|
+
* names drift between minor versions. Everything version-sensitive is confined to
|
|
20
|
+
* {@link BuildTurnArgs} and the event names in {@link MapEvent}, so adapting to a new Codex release
|
|
21
|
+
* is a change in one small place rather than a redesign — and `AIAgentHarness.ExecutablePath` lets a
|
|
22
|
+
* deployment pin a specific binary without touching code at all.
|
|
23
|
+
*/
|
|
24
|
+
let CodexAdapter = class CodexAdapter extends BaseCliHarnessAdapter {
|
|
25
|
+
constructor() {
|
|
26
|
+
super(...arguments);
|
|
27
|
+
this.executable = 'codex';
|
|
28
|
+
}
|
|
29
|
+
get ExecutablePath() {
|
|
30
|
+
return this.executable;
|
|
31
|
+
}
|
|
32
|
+
/** @inheritdoc */
|
|
33
|
+
get Capabilities() {
|
|
34
|
+
return {
|
|
35
|
+
// Codex resumes by threading its session id back on the next invocation.
|
|
36
|
+
SessionResume: true,
|
|
37
|
+
// No constrained-output mode: the turn-end JSON envelope is coaxed by prompt and
|
|
38
|
+
// recovered by BaseAgent's malformed-response retry when the model drifts into prose.
|
|
39
|
+
StructuredOutput: false,
|
|
40
|
+
UsageReporting: true,
|
|
41
|
+
// FALSE: this adapter does not translate MJ's permission policy into harness flags, so a
|
|
42
|
+
// configured posture is INERT here and the runtime warns about it. Not an oversight —
|
|
43
|
+
// Codex CLI's permission flags could not be verified against a real install, and
|
|
44
|
+
// guessing them produces the exact failure this flag exists to surface: a policy that
|
|
45
|
+
// looks applied and is not. Verify against the CLI, then implement
|
|
46
|
+
// ApplyPermissionPolicy and flip this to true.
|
|
47
|
+
PermissionPolicy: false,
|
|
48
|
+
PermissionHooks: false,
|
|
49
|
+
McpClient: true,
|
|
50
|
+
WorkspaceScoping: true,
|
|
51
|
+
ModelSelection: true,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
BuildTurnArgs(input, isFirstTurn) {
|
|
55
|
+
const args = ['exec', '--json'];
|
|
56
|
+
if (!isFirstTurn && this.sessionId) {
|
|
57
|
+
args.push('--resume', this.sessionId);
|
|
58
|
+
}
|
|
59
|
+
if (this.config?.Model) {
|
|
60
|
+
args.push('--model', this.config.Model);
|
|
61
|
+
}
|
|
62
|
+
args.push(input);
|
|
63
|
+
return args;
|
|
64
|
+
}
|
|
65
|
+
MapEvent(raw) {
|
|
66
|
+
// Capture the session id wherever it first appears so the next turn can resume, and so the
|
|
67
|
+
// run can persist it to AIAgentRun.ExternalSessionID for vendor-side log correlation.
|
|
68
|
+
const session = this.readString(raw, 'session_id') ?? this.readString(raw, 'sessionId');
|
|
69
|
+
if (session) {
|
|
70
|
+
this.sessionId = session;
|
|
71
|
+
}
|
|
72
|
+
switch (this.readString(raw, 'type')) {
|
|
73
|
+
case 'assistant_message':
|
|
74
|
+
case 'agent_message': {
|
|
75
|
+
const text = this.readString(raw, 'message') ?? this.readString(raw, 'text');
|
|
76
|
+
return text ? { Type: 'assistant-text', Text: text } : null;
|
|
77
|
+
}
|
|
78
|
+
case 'tool_call':
|
|
79
|
+
case 'exec_command': {
|
|
80
|
+
const description = this.readString(raw, 'command') ?? this.readString(raw, 'name') ?? 'sandbox activity';
|
|
81
|
+
return { Type: 'sandbox-activity', Description: description };
|
|
82
|
+
}
|
|
83
|
+
case 'token_count':
|
|
84
|
+
case 'usage': {
|
|
85
|
+
const usage = this.readObject(raw, 'usage') ?? raw;
|
|
86
|
+
return {
|
|
87
|
+
Type: 'usage',
|
|
88
|
+
InputTokens: this.readNumber(usage, 'input_tokens') ?? 0,
|
|
89
|
+
OutputTokens: this.readNumber(usage, 'output_tokens') ?? 0,
|
|
90
|
+
CostUsd: this.readNumber(usage, 'cost_usd'),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
case 'task_complete':
|
|
94
|
+
case 'turn_complete': {
|
|
95
|
+
const text = this.readString(raw, 'last_agent_message') ??
|
|
96
|
+
this.readString(raw, 'message') ??
|
|
97
|
+
this.readString(raw, 'text') ??
|
|
98
|
+
'';
|
|
99
|
+
return { Type: 'turn-complete', RawText: text };
|
|
100
|
+
}
|
|
101
|
+
case 'error': {
|
|
102
|
+
return { Type: 'session-error', Error: this.readString(raw, 'message') ?? 'Codex reported an error' };
|
|
103
|
+
}
|
|
104
|
+
default:
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** Points the adapter at a specific binary, from `AIAgentHarness.ExecutablePath`. */
|
|
109
|
+
SetExecutable(path) {
|
|
110
|
+
this.executable = path;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
CodexAdapter = __decorate([
|
|
114
|
+
RegisterClass(BaseHarnessAdapter, 'CodexAdapter')
|
|
115
|
+
], CodexAdapter);
|
|
116
|
+
export { CodexAdapter };
|
|
117
|
+
//# sourceMappingURL=CodexAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CodexAdapter.js","sourceRoot":"","sources":["../../src/adapters/CodexAdapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { BaseCliHarnessAdapter, HarnessCliRawEvent } from './BaseCliHarnessAdapter.js';
|
|
2
|
+
import { HarnessCapabilities, HarnessTurnEvent } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Drives Google's Gemini CLI (`@google/gemini-cli`).
|
|
5
|
+
*
|
|
6
|
+
* `@google/gemini-cli-core` does expose a compilable entry point, but it is the CLI's internals
|
|
7
|
+
* rather than a supported embedding API — depending on it would couple MJ to a package with no
|
|
8
|
+
* stability contract. The CLI surface is the supported one, so this adapter uses it.
|
|
9
|
+
*
|
|
10
|
+
* Gemini CLI is the weakest of the five on session continuity: it is built around one-shot
|
|
11
|
+
* invocations. {@link Capabilities} reports `SessionResume: false` accordingly, which makes the
|
|
12
|
+
* runtime replay accumulated context into each turn — correct, but the token cost grows with turn
|
|
13
|
+
* count, and reporting it honestly here is what lets the run's cost guardrail see that.
|
|
14
|
+
*/
|
|
15
|
+
export declare class GeminiCliAdapter extends BaseCliHarnessAdapter {
|
|
16
|
+
private executable;
|
|
17
|
+
protected get ExecutablePath(): string;
|
|
18
|
+
/** @inheritdoc */
|
|
19
|
+
get Capabilities(): HarnessCapabilities;
|
|
20
|
+
protected BuildTurnArgs(input: string, _isFirstTurn: boolean): string[];
|
|
21
|
+
protected MapEvent(raw: HarnessCliRawEvent): HarnessTurnEvent | null;
|
|
22
|
+
/** Points the adapter at a specific binary, from `AIAgentHarness.ExecutablePath`. */
|
|
23
|
+
SetExecutable(path: string): void;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=GeminiCliAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GeminiCliAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/GeminiCliAdapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEvF,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpE;;;;;;;;;;;GAWG;AACH,qBACa,gBAAiB,SAAQ,qBAAqB;IACvD,OAAO,CAAC,UAAU,CAAY;IAE9B,SAAS,KAAK,cAAc,IAAI,MAAM,CAErC;IAED,kBAAkB;IAClB,IAAW,YAAY,IAAI,mBAAmB,CAkB7C;IAED,SAAS,CAAC,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,GAAG,MAAM,EAAE;IAUvE,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,kBAAkB,GAAG,gBAAgB,GAAG,IAAI;IAgCpE,qFAAqF;IAC9E,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;CAG3C"}
|