@mikode13/harness 9.3.1 → 10.0.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.
- package/README.md +63 -7
- package/dist/agent/domain/agent.d.ts +11 -5
- package/dist/agent/domain/agent.js.map +1 -1
- package/dist/agent/domain/approval.d.ts +2 -1
- package/dist/agent/domain/approval.js +2 -1
- package/dist/agent/domain/approval.js.map +1 -1
- package/dist/agent/domain/question.d.ts +5 -0
- package/dist/agent/domain/question.js +11 -0
- package/dist/agent/domain/question.js.map +1 -1
- package/dist/agent/domain/runContext.d.ts +17 -0
- package/dist/agent/domain/runContext.js +9 -0
- package/dist/agent/domain/runContext.js.map +1 -1
- package/dist/agent/domain/transcript.d.ts +10 -0
- package/dist/agent/domain/transcript.js.map +1 -1
- package/dist/engines/domain/model/askUser.d.ts +0 -5
- package/dist/engines/domain/model/askUser.js +0 -9
- package/dist/engines/domain/model/askUser.js.map +1 -1
- package/dist/engines/domain/model/llmAgent.d.ts +19 -3
- package/dist/engines/domain/model/llmAgent.js +141 -51
- package/dist/engines/domain/model/llmAgent.js.map +1 -1
- package/dist/factory/infrastructure/agentLLMFactory.d.ts +6 -2
- package/dist/factory/infrastructure/agentLLMFactory.js +1 -1
- package/dist/factory/infrastructure/agentLLMFactory.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/tools/domain/preparedCall.d.ts +14 -0
- package/dist/tools/domain/preparedCall.js +12 -0
- package/dist/tools/domain/preparedCall.js.map +1 -1
- package/dist/tools/domain/readRegistry.d.ts +7 -0
- package/dist/tools/domain/readRegistry.js.map +1 -1
- package/dist/tools/infrastructure/agentAsTool.d.ts +61 -0
- package/dist/tools/infrastructure/agentAsTool.js +144 -0
- package/dist/tools/infrastructure/agentAsTool.js.map +1 -0
- package/dist/tools/infrastructure/commandTool.js +6 -2
- package/dist/tools/infrastructure/commandTool.js.map +1 -1
- package/dist/tools/infrastructure/exploreTool.js +9 -22
- package/dist/tools/infrastructure/exploreTool.js.map +1 -1
- package/dist/tools/infrastructure/fileEditor.d.ts +2 -0
- package/dist/tools/infrastructure/fileEditor.js +5 -1
- package/dist/tools/infrastructure/fileEditor.js.map +1 -1
- package/dist/tools/infrastructure/fileFormatter.d.ts +1 -0
- package/dist/tools/infrastructure/fileFormatter.js +5 -1
- package/dist/tools/infrastructure/fileFormatter.js.map +1 -1
- package/dist/tools/infrastructure/subagents.d.ts +19 -0
- package/dist/tools/infrastructure/subagents.js +29 -0
- package/dist/tools/infrastructure/subagents.js.map +1 -0
- package/dist/tools/infrastructure/writeSession.d.ts +8 -0
- package/dist/tools/infrastructure/writeSession.js +37 -0
- package/dist/tools/infrastructure/writeSession.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,7 +43,9 @@ chat loop itself never knows the difference.
|
|
|
43
43
|
harness ships tools that read the repository (list, search, read), bounded by
|
|
44
44
|
`.gitignore`, and `createFileTools`, which also change files in each provider's
|
|
45
45
|
native format, every change recorded so it can be undone, and run the commands you
|
|
46
|
-
configure, such as the tests, recording what each one changed too.
|
|
46
|
+
configure, such as the tests, recording what each one changed too. `agentAsTool` offers
|
|
47
|
+
one agent to another as a tool, such as a cheaper committer to a coding agent, working
|
|
48
|
+
inside the caller's run.
|
|
47
49
|
- **`OrchestratorAgent`**: coordinates a planner, an executor, and a reviewer
|
|
48
50
|
(each an injected `Agent`) in a plan → execute → review loop. The reviewer's
|
|
49
51
|
decision is a Zod-validated structured `{decision, feedback}`, not free text —
|
|
@@ -495,10 +497,12 @@ run in `AgentResponse.runId`. Progress reports each change as a unified `diff` o
|
|
|
495
497
|
|
|
496
498
|
For debugging, `onTranscript` receives what the model saw in each run once the run ended,
|
|
497
499
|
completed, failed or cancelled. A `Transcript` has the run's `outcome`, the role it was of in
|
|
498
|
-
`createLLMOrchestrator`, or `explorer`, and a `content` of plain JSON: the instructions, the
|
|
499
|
-
definitions, the run's messages with what the tools returned, and the error a run that did
|
|
500
|
-
complete ended with.
|
|
501
|
-
|
|
500
|
+
`createLLMOrchestrator`, or `explorer`, and a `content` of plain JSON: the instructions, the
|
|
501
|
+
tool definitions, the run's messages with what the tools returned, and the error a run that did
|
|
502
|
+
not complete ended with. The transcript of an agent another agent handed a task to also has
|
|
503
|
+
`delegation`: the tool, the call's id and the subagent's number that started it. `content`
|
|
504
|
+
has no stable shape; read it, but do not build on it. A callback that fails is logged, and the
|
|
505
|
+
run stays as it ended.
|
|
502
506
|
|
|
503
507
|
`createLLMOrchestrator()` takes the options of `createOrchestrator()` plus `workspace`,
|
|
504
508
|
`maxSteps`, `compactAt`, `commands`, `formatter`, `summarizeUndone` and `onTranscript`, and returns a promise. Every role runs on the
|
|
@@ -550,6 +554,56 @@ createLLMOrchestrator({ workspace, compactAt: { executor: 200_000 } });
|
|
|
550
554
|
Without `workspace` it works on the current directory, read only. One run of the
|
|
551
555
|
orchestrator is one run of the history, every round included.
|
|
552
556
|
|
|
557
|
+
### Agents that hand work to other agents
|
|
558
|
+
|
|
559
|
+
`agentAsTool` offers an agent to another one as a tool. The calling model writes the task, and
|
|
560
|
+
a new agent from the `createAgent` option runs it; its answer is the call's result. Specialists can come
|
|
561
|
+
from a package you reuse in every project, or be written in one project:
|
|
562
|
+
|
|
563
|
+
```ts
|
|
564
|
+
import { agentAsTool, createFileTools, createLLMAgent } from '@mikode13/harness';
|
|
565
|
+
|
|
566
|
+
const tools = await createFileTools('anthropic', workspace, { commands });
|
|
567
|
+
const commit = agentAsTool({
|
|
568
|
+
name: 'commit',
|
|
569
|
+
description: 'Commits the work done so far, with a message that says why.',
|
|
570
|
+
createAgent: () =>
|
|
571
|
+
createLLMAgent('anthropic', {
|
|
572
|
+
model: 'haiku',
|
|
573
|
+
systemPrompt: 'You commit the changes you are told about, one commit, with a clear message.',
|
|
574
|
+
tools,
|
|
575
|
+
}),
|
|
576
|
+
});
|
|
577
|
+
const coder = createLLMAgent('anthropic', {
|
|
578
|
+
systemPrompt: 'You write code, with a test for each change, and commit when the tests pass.',
|
|
579
|
+
tools: [...tools, commit],
|
|
580
|
+
workspace,
|
|
581
|
+
});
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
The agent works inside the caller's run:
|
|
585
|
+
|
|
586
|
+
- what it writes is the same run of the history, so one undo reverts both; it keeps its own
|
|
587
|
+
tools' access policy, and edits only what it read itself;
|
|
588
|
+
- what it spends counts in the caller's `tokens`, a failed attempt included;
|
|
589
|
+
- it asks and is approved through the run's `ask` and `approve`, which agents working at once
|
|
590
|
+
may call before an earlier call has its answer; one person at one terminal needs them queued,
|
|
591
|
+
as the CLI does. Delegating never grants `autoApprove`; the agent has it only if the
|
|
592
|
+
`createAgent` option gave it. Handing over a task asks no one unless you set
|
|
593
|
+
`risk: 'destructive'`;
|
|
594
|
+
- its progress arrives as `subagent` events of the call, with the tool's `name`, and its
|
|
595
|
+
transcript names the call in `delegation`.
|
|
596
|
+
|
|
597
|
+
Calls to these tools next to each other in one answer run at once; a call to another tool
|
|
598
|
+
between them waits for those before it, and the ones after it wait for it. The history stays
|
|
599
|
+
exact, because every change and command of a run is made one at a time, but agents working at
|
|
600
|
+
once can see each other's unfinished work, and a long command holds the others' writes until it
|
|
601
|
+
ends. An agent that fails
|
|
602
|
+
is the call's error, for the model to act on; a cancellation, or your approver or asker failing
|
|
603
|
+
inside it, ends the run. A call deeper than `maxDepth`, 2 by default, is refused. Only agents
|
|
604
|
+
from `createLLMAgent` work this way: an Agent SDK agent from the `createAgent` factory runs
|
|
605
|
+
as a run of its own.
|
|
606
|
+
|
|
553
607
|
### Running the project's commands
|
|
554
608
|
|
|
555
609
|
With `commands`, `createFileTools` and `createLLMOrchestrator` add `runCommand`, which runs
|
|
@@ -750,8 +804,10 @@ heading. Reasoning is dim italic and complete by default; pass
|
|
|
750
804
|
updates the spinner rather than printing a second line, and cancelling a turn clears
|
|
751
805
|
its pending spinner state. Pass `--steps <file>` to append one JSON line per call to the
|
|
752
806
|
model to that file: the call's tokens and what its context held, by origin, for measuring
|
|
753
|
-
what a run spends.
|
|
754
|
-
|
|
807
|
+
what a run spends. A subagent's call is written as the `subagent` events that carry it, which
|
|
808
|
+
name the role and the agents that led to it. Each subagent's calls and messages are shown as a
|
|
809
|
+
thread, in a color of its own, such as `planner › explorer 2`, and questions and approvals of
|
|
810
|
+
agents working at once take turns. Pass `--debug <folder>` to keep,
|
|
755
811
|
in a folder for the session under it, every progress event in `events.jsonl` and, with
|
|
756
812
|
`--llm`, the transcript of every run of every agent, one numbered file each, such as
|
|
757
813
|
`003-executor-failed.json`. A failed tool call's event says what the model was told, in
|
|
@@ -40,13 +40,15 @@ export interface RunOptions {
|
|
|
40
40
|
/**
|
|
41
41
|
* Asked before a model-backed agent runs a `destructive` tool call. Without it, such a call
|
|
42
42
|
* is denied. An agent built with `autoApprove` never asks, and the Agent SDK engines ignore
|
|
43
|
-
* it: their own permission systems decide.
|
|
43
|
+
* it: their own permission systems decide. Agents a delegation runs at once may call it before
|
|
44
|
+
* an earlier call has its answer.
|
|
44
45
|
*/
|
|
45
46
|
approve?: Approver | undefined;
|
|
46
47
|
/**
|
|
47
48
|
* Answers what a model-backed agent asks the user, through its `askUser` tool, when a
|
|
48
49
|
* decision is the user's to make. Without it the agent has no such tool. The Agent SDK
|
|
49
|
-
* engines ignore it.
|
|
50
|
+
* engines ignore it. Agents a delegation runs at once may call it before an earlier call has
|
|
51
|
+
* its answer.
|
|
50
52
|
*/
|
|
51
53
|
ask?: Asker | undefined;
|
|
52
54
|
}
|
|
@@ -163,14 +165,18 @@ export type ProgressEvent = {
|
|
|
163
165
|
agent?: ProgressAgent;
|
|
164
166
|
}
|
|
165
167
|
/**
|
|
166
|
-
* An event of a subagent that a tool call started
|
|
167
|
-
*
|
|
168
|
-
* numbers its subagents from 1, so the events of agents
|
|
168
|
+
* An event of a subagent that a tool call started: one of the explorers an `explore` call runs
|
|
169
|
+
* at once, or the agent a tool from `agentAsTool` delegates to. `callId` is the `id` of that
|
|
170
|
+
* call's `tool` events, and `subagent` numbers its subagents from 1, so the events of agents
|
|
171
|
+
* running in parallel stay apart. A subagent's own subagents arrive as `subagent` events
|
|
172
|
+
* inside this one's `event`.
|
|
169
173
|
*/
|
|
170
174
|
| {
|
|
171
175
|
type: 'subagent';
|
|
172
176
|
callId: string;
|
|
173
177
|
subagent: number;
|
|
178
|
+
/** Which agent it is: `explorer`, or the name of the tool that delegates to it. */
|
|
179
|
+
name: string;
|
|
174
180
|
/** What the subagent was asked. */
|
|
175
181
|
task: string;
|
|
176
182
|
event: ProgressEvent;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../../src/agent/domain/agent.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../../src/agent/domain/agent.ts"],"names":[],"mappings":"AA0DA,gDAAgD;AAChD,MAAM,CAAC,MAAM,cAAc,GAAa,GAAG,EAAE,CAAC,SAAS,CAAC","sourcesContent":["import type { Tokens } from '#src/shared/domain/tokens';\nimport type { Approver } from './approval.ts';\nimport type { Asker } from './question.ts';\n\n/**\n * How a run ended when it did not fail. Every token a run spends travels with its end: here\n * when it finishes, in `RecoverableError.tokens` or `UnrecoverableError.tokens` when it fails.\n */\nexport interface AgentResponse {\n\t/** The final text. Empty when the run produced none, for example a turn that only edited files. */\n\tresponse: string;\n\t/**\n\t * Missing when any call in the run completed without reporting usage, which is not the same\n\t * as zero: a partial sum would look complete.\n\t */\n\ttokens?: Tokens;\n\t/** Wall-clock seconds the consumer waited for the run, retries and every role included. */\n\tduration: number;\n\t/**\n\t * The run the workspace's history recorded, when this run wrote through the harness's write\n\t * tools. Absent for a run that wrote nothing, and from the Agent SDK engines, whose changes\n\t * the history does not record.\n\t */\n\trunId?: string;\n}\n\nexport type Callback = (item: ProgressEvent) => void;\n\n/**\n * What a run needs besides its prompt. InstructedAgent passes it on whole. OrchestratorAgent\n * copies its own enumerable fields and replaces only `onProgress` per role; RetryingAgent does\n * the same for a run with `ask`, replacing only `ask`. A field must therefore be an own\n * enumerable one to reach every agent.\n */\nexport interface RunOptions {\n\t/**\n\t * Cancels the run, which then rejects with the signal's reason: an `AbortError`, or a\n\t * `TimeoutError` from `AbortSignal.timeout`. `isAbortError` recognizes both.\n\t */\n\tsignal: AbortSignal;\n\t/** Receives the run's live activity. Without it, the run is silent. */\n\tonProgress?: Callback | undefined;\n\t/**\n\t * Asked before a model-backed agent runs a `destructive` tool call. Without it, such a call\n\t * is denied. An agent built with `autoApprove` never asks, and the Agent SDK engines ignore\n\t * it: their own permission systems decide. Agents a delegation runs at once may call it before\n\t * an earlier call has its answer.\n\t */\n\tapprove?: Approver | undefined;\n\t/**\n\t * Answers what a model-backed agent asks the user, through its `askUser` tool, when a\n\t * decision is the user's to make. Without it the agent has no such tool. The Agent SDK\n\t * engines ignore it. Agents a delegation runs at once may call it before an earlier call has\n\t * its answer.\n\t */\n\task?: Asker | undefined;\n}\n\n/** The `onProgress` of a run that gave none. */\nexport const ignoreProgress: Callback = () => undefined;\n\n/**\n * Live activity during a run, for narration only: the result travels in `AgentResponse`.\n * New event types can arrive in a minor release, so render the types you know and ignore\n * the rest instead of switching over them exhaustively with a `never` check.\n */\nexport type ProgressEvent =\n\t| { type: 'command'; command: string; exitCode?: number; agent?: ProgressAgent }\n\t| { type: 'reasoning'; message: string; agent?: ProgressAgent }\n\t| { type: 'search'; query: string; agent?: ProgressAgent }\n\t| {\n\t\t\ttype: 'fileChange';\n\t\t\tchanges: { path: string; kind: 'add' | 'update' | 'delete' }[];\n\t\t\tagent?: ProgressAgent;\n\t }\n\t| { type: 'mcpTool'; server: string; tool: string; status: string; agent?: ProgressAgent }\n\t| { type: 'agentMessage'; message: string; agent?: ProgressAgent }\n\t/** `id` pairs a call's end with its start when one step calls the same tool more than once. */\n\t| {\n\t\t\ttype: 'tool';\n\t\t\tid: string;\n\t\t\tname: string;\n\t\t\tagent?: ProgressAgent;\n\t\t\t/** A short, tool-owned description of the call. Narration only. */\n\t\t\tsummary?: string;\n\t\t\t/** The exit code of a configured command that ended with an error. Narration only. */\n\t\t\texitCode?: number;\n\t\t\t/**\n\t\t\t * On `error`, for an agent on the model APIs: what the model was told went wrong, such\n\t\t\t * as a patch that did not apply or a failed command's output. Narration only.\n\t\t\t */\n\t\t\terror?: string;\n\t\t\t/**\n\t\t\t * `in_progress` only once the call is about to run, after any approval. `denied` when\n\t\t\t * it never ran because no one allowed it, with no `in_progress` before it.\n\t\t\t */\n\t\t\tstatus: 'in_progress' | 'completed' | 'error' | 'denied';\n\t\t\t/**\n\t\t\t * On `completed`, for a call that changed a file through the harness's write tools: the\n\t\t\t * change as a unified diff, for the person watching. Narration only; the run's whole\n\t\t\t * record is the history's `changes`.\n\t\t\t */\n\t\t\tdiff?: string;\n\t }\n\t| { type: 'todoList'; items: { text: string; completed: boolean }[]; agent?: ProgressAgent }\n\t| { type: 'turnStarted'; agent?: ProgressAgent }\n\t| { type: 'turnEnded'; agent?: ProgressAgent }\n\t/**\n\t * One billed call to the model by an agent on the model APIs, including one the model\n\t * stopped early or the client turned into an error: what the call sent and what it cost.\n\t * Narration only: the run's total is `AgentResponse.tokens`, or the failure's `tokens`.\n\t */\n\t| {\n\t\t\ttype: 'step';\n\t\t\t/** 1 for the first call of the run. */\n\t\t\tstep: number;\n\t\t\t/** Missing when the provider did not report this call's usage. */\n\t\t\ttokens?: Tokens;\n\t\t\tcontext: StepContext;\n\t\t\tagent?: ProgressAgent;\n\t }\n\t/**\n\t * A workflow's stage asks its agent for an answer; `attempt` is 2 or more when the previous\n\t * answer was invalid. Stages run one at a time, so the events that follow, until the next\n\t * workflow event, are this stage's agent's.\n\t */\n\t| { type: 'workflowStage'; stage: string; attempt: number; agent?: ProgressAgent }\n\t/** The transition a stage's valid answer selected, and the stage it leads to. */\n\t| {\n\t\t\ttype: 'workflowTransition';\n\t\t\tstage: string;\n\t\t\ttransition: 'go' | 'stop' | 'complete';\n\t\t\t/** Absent after `stop` or `complete`. */\n\t\t\tnextStage?: string;\n\t\t\tagent?: ProgressAgent;\n\t }\n\t/**\n\t * An event of a subagent that a tool call started: one of the explorers an `explore` call runs\n\t * at once, or the agent a tool from `agentAsTool` delegates to. `callId` is the `id` of that\n\t * call's `tool` events, and `subagent` numbers its subagents from 1, so the events of agents\n\t * running in parallel stay apart. A subagent's own subagents arrive as `subagent` events\n\t * inside this one's `event`.\n\t */\n\t| {\n\t\t\ttype: 'subagent';\n\t\t\tcallId: string;\n\t\t\tsubagent: number;\n\t\t\t/** Which agent it is: `explorer`, or the name of the tool that delegates to it. */\n\t\t\tname: string;\n\t\t\t/** What the subagent was asked. */\n\t\t\ttask: string;\n\t\t\tevent: ProgressEvent;\n\t\t\tagent?: ProgressAgent;\n\t };\n\n/**\n * What one call to the model sent, in characters by where it came from: an estimate of size\n * for finding what fills a context, not a token count.\n */\nexport interface StepContext {\n\t/** The system prompt. */\n\tinstructions: number;\n\t/** The tool definitions. */\n\ttools: number;\n\t/**\n\t * The text of the user's prompts and the model's answers, and the provider's summary of the\n\t * turns before it once it has compacted them: only what the call sent.\n\t */\n\tmessages: number;\n\t/** Reasoning the model produced earlier and is sent back: its provider's own signed or encrypted blocks. */\n\treasoning: number;\n\t/** The input of every tool call so far. */\n\ttoolCalls: number;\n\t/** Every tool result so far, by tool, largest first. */\n\ttoolResults: { tool: string; characters: number; calls: number }[];\n\t/** The largest single results, each with its call's summary when the tool gives one. */\n\tlargestResults: { tool: string; characters: number; summary?: string }[];\n}\n\n/** A role in the fixed planner → executor → reviewer workflow. */\nexport type ProgressAgent = 'planner' | 'executor' | 'reviewer';\n\n/**\n * The contract every engine (CodexAgent, ClaudeAgent, future providers) and every\n * decorator (RetryingAgent, OrchestratorAgent) is built against.\n *\n * Implementers MUST only ever reject with `RecoverableError` or `UnrecoverableError`\n * (see src/shared/domain/errors.ts) — never a raw SDK error, a plain `Error`, or anything else leaked\n * unclassified. Every consumer of `Agent` (RetryingAgent's retry decision,\n * OrchestratorAgent's failure handling) `instanceof`-checks against those two types to\n * decide what to do next; a leaked, unclassified error bypasses that decision\n * entirely — it gets retried when it shouldn't be, or crashes a run that a retry\n * would have recovered. Wrap every call into the underlying SDK so nothing escapes\n * unclassified, including failures the SDK itself doesn't model as a domain error\n * (network errors, malformed responses, etc.); `classifyProviderFailure` does this.\n *\n * Cancellation is the one exception: an `AbortError`, or the `TimeoutError` of\n * `AbortSignal.timeout`, must propagate unchanged, because\n * consumers check for it before either error type.\n */\nexport interface Agent {\n\trun(prompt: string, options: RunOptions): Promise<AgentResponse>;\n}\n"]}
|
|
@@ -46,7 +46,8 @@ export type RememberableDecision = {
|
|
|
46
46
|
* Wraps `ask` so a call the user allowed with `remember` is not asked about again. `key` decides
|
|
47
47
|
* what counts as the same call; it defaults to the tool's name, so "always" allows every
|
|
48
48
|
* destructive call to that tool. The memory lives in the returned approver, so its lifetime is
|
|
49
|
-
* the consumer's choice: one per session, one per user, or one per run.
|
|
49
|
+
* the consumer's choice: one per session, one per user, or one per run. Calls made at once are
|
|
50
|
+
* each asked; a call that waits its turn in front of it finds an "always" given meanwhile.
|
|
50
51
|
*/
|
|
51
52
|
export declare function rememberApprovals(ask: (request: ApprovalRequest, signal: AbortSignal) => RememberableDecision | Promise<RememberableDecision>, { key }?: {
|
|
52
53
|
key?: (request: ApprovalRequest) => string;
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Wraps `ask` so a call the user allowed with `remember` is not asked about again. `key` decides
|
|
3
3
|
* what counts as the same call; it defaults to the tool's name, so "always" allows every
|
|
4
4
|
* destructive call to that tool. The memory lives in the returned approver, so its lifetime is
|
|
5
|
-
* the consumer's choice: one per session, one per user, or one per run.
|
|
5
|
+
* the consumer's choice: one per session, one per user, or one per run. Calls made at once are
|
|
6
|
+
* each asked; a call that waits its turn in front of it finds an "always" given meanwhile.
|
|
6
7
|
*/
|
|
7
8
|
export function rememberApprovals(ask, { key = request => request.tool } = {}) {
|
|
8
9
|
const allowed = new Set();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval.js","sourceRoot":"","sources":["../../../src/agent/domain/approval.ts"],"names":[],"mappings":"AA6CA
|
|
1
|
+
{"version":3,"file":"approval.js","sourceRoot":"","sources":["../../../src/agent/domain/approval.ts"],"names":[],"mappings":"AA6CA;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAChC,GAGyD,EACzD,EAAE,GAAG,GAAG,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,KAAqD,EAAE;IAEtF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAElC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;QAChC,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;QACxB,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAE/C,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1C,0FAA0F;QAC1F,MAAM,CAAC,cAAc,EAAE,CAAC;QACxB,8FAA8F;QAC9F,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI,CAAC,QAAQ,CAAC,QAAQ;YAAE,OAAO,QAAQ,CAAC;QACxC,IAAI,YAAY,CAAC,MAAM,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1C,OAAO,QAAQ,CAAC;IACjB,CAAC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAAe;IAC3C,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACnD,MAAM,IAAI,SAAS,CAAC,0CAA0C,CAAC,CAAC;IACjE,CAAC;IACD,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAkD,CAAC;IAChF,IAAI,OAAO,QAAQ,KAAK,SAAS,EAAE,CAAC;QACnC,MAAM,IAAI,SAAS,CAAC,oDAAoD,CAAC,CAAC;IAC3E,CAAC;IAED,OAAO,QAAQ;QACd,CAAC,CAAC,EAAE,QAAQ,EAAE;QACd,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;AAC1E,CAAC;AAED,SAAS,YAAY,CAAC,MAAe;IACpC,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAgC,CAAC;IACtD,IAAI,QAAQ,KAAK,SAAS,IAAI,OAAO,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC7D,MAAM,IAAI,SAAS,CAAC,oDAAoD,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,QAAQ,KAAK,IAAI,CAAC;AAC1B,CAAC","sourcesContent":["/**\n * How much one tool call can hurt, judged by the tool from that call's input:\n * - `safe` changes nothing, as with a read.\n * - `mutating` makes a change git can undo, as with creating a file.\n * - `destructive` loses something, as with overwriting or deleting a file.\n */\nexport type ToolRisk = 'safe' | 'mutating' | 'destructive';\n\n/** A program a tool call will run, exactly as it will run: no shell reads it. */\nexport interface CommandLine {\n\texecutable: string;\n\targs: string[];\n\t/** Where it runs, as a real path. */\n\tcwd: string;\n}\n\n/** A tool call waiting for someone to allow it. */\nexport interface ApprovalRequest {\n\ttool: string;\n\t/** What the model sent, unvalidated: the tool validates it only if the call is allowed. */\n\tinput: unknown;\n\trisk: ToolRisk;\n\t/** For a call that runs a configured command: the program, worked out from the input. */\n\tcommand?: CommandLine;\n}\n\n/** A denial's `reason` reaches the model, so it can try something else. */\nexport type ApprovalDecision =\n\t{ approved: true } | { approved: false; reason?: string | undefined };\n\n/**\n * Decides one tool call at a time. A consumer that wants \"don't ask again\" remembers its own\n * answers, so the harness keeps no session state. A throw ends the run as an\n * `UnrecoverableError`.\n */\nexport type Approver = (\n\trequest: ApprovalRequest,\n\tsignal: AbortSignal,\n) => ApprovalDecision | Promise<ApprovalDecision>;\n\n/** What `rememberApprovals` asks for: `remember` allows the same call again without asking. */\nexport type RememberableDecision =\n\t| { approved: true; remember?: boolean | undefined }\n\t| { approved: false; reason?: string | undefined };\n\n/**\n * Wraps `ask` so a call the user allowed with `remember` is not asked about again. `key` decides\n * what counts as the same call; it defaults to the tool's name, so \"always\" allows every\n * destructive call to that tool. The memory lives in the returned approver, so its lifetime is\n * the consumer's choice: one per session, one per user, or one per run. Calls made at once are\n * each asked; a call that waits its turn in front of it finds an \"always\" given meanwhile.\n */\nexport function rememberApprovals(\n\task: (\n\t\trequest: ApprovalRequest,\n\t\tsignal: AbortSignal,\n\t) => RememberableDecision | Promise<RememberableDecision>,\n\t{ key = request => request.tool }: { key?: (request: ApprovalRequest) => string } = {},\n): Approver {\n\tconst allowed = new Set<string>();\n\n\treturn async (request, signal) => {\n\t\tconst id = key(request);\n\t\tif (allowed.has(id)) return { approved: true };\n\n\t\tconst answer = await ask(request, signal);\n\t\t// The agent discards an answer that arrives after a cancellation, so it must not be kept.\n\t\tsignal.throwIfAborted();\n\t\t// Read before keeping anything: a truthy `'false'` would otherwise be allowed and remembered.\n\t\tconst decision = readDecision(answer);\n\t\tif (!decision.approved) return decision;\n\t\tif (readRemember(answer)) allowed.add(id);\n\t\treturn decision;\n\t};\n}\n\n/**\n * Reads an approver's answer without trusting its type, since a JavaScript consumer can return\n * anything: only an `approved` that is a boolean decides, and anything else throws.\n */\nexport function readDecision(answer: unknown): ApprovalDecision {\n\tif (typeof answer !== 'object' || answer === null) {\n\t\tthrow new TypeError('The approver answered without a decision');\n\t}\n\tconst { approved, reason } = answer as { approved?: unknown; reason?: unknown };\n\tif (typeof approved !== 'boolean') {\n\t\tthrow new TypeError('The approver answered without a boolean `approved`');\n\t}\n\n\treturn approved\n\t\t? { approved }\n\t\t: { approved, reason: typeof reason === 'string' ? reason : undefined };\n}\n\nfunction readRemember(answer: unknown): boolean {\n\tconst { remember } = answer as { remember?: unknown };\n\tif (remember !== undefined && typeof remember !== 'boolean') {\n\t\tthrow new TypeError('The approver answered without a boolean `remember`');\n\t}\n\treturn remember === true;\n}\n"]}
|
|
@@ -17,6 +17,11 @@ export interface UserQuestion {
|
|
|
17
17
|
* `UnrecoverableError`, as a failing approver does.
|
|
18
18
|
*/
|
|
19
19
|
export type Asker = (questions: UserQuestion[], signal: AbortSignal) => string[] | Promise<string[]>;
|
|
20
|
+
/**
|
|
21
|
+
* The asker's answers, one non-empty string per question, or nothing: an asker is host code, so
|
|
22
|
+
* an answer of any other shape is its bug, not the model's.
|
|
23
|
+
*/
|
|
24
|
+
export declare function readAnswers(answers: unknown, questions: UserQuestion[]): string[] | undefined;
|
|
20
25
|
/** What one run's user answered, as `rememberAnswers` keeps it. */
|
|
21
26
|
export interface AnsweredQuestion {
|
|
22
27
|
question: string;
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The asker's answers, one non-empty string per question, or nothing: an asker is host code, so
|
|
3
|
+
* an answer of any other shape is its bug, not the model's.
|
|
4
|
+
*/
|
|
5
|
+
export function readAnswers(answers, questions) {
|
|
6
|
+
if (!Array.isArray(answers) || answers.length !== questions.length)
|
|
7
|
+
return undefined;
|
|
8
|
+
return answers.every(answer => typeof answer === 'string' && answer.trim() !== '')
|
|
9
|
+
? answers
|
|
10
|
+
: undefined;
|
|
11
|
+
}
|
|
1
12
|
/**
|
|
2
13
|
* Wraps `ask` so a question asked again with the same words gets the answer the user already
|
|
3
14
|
* gave, and only the others are asked. `answered` holds every answer, in the order given. The
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"question.js","sourceRoot":"","sources":["../../../src/agent/domain/question.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"question.js","sourceRoot":"","sources":["../../../src/agent/domain/question.ts"],"names":[],"mappings":"AAyBA;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,OAAgB,EAAE,SAAyB;IACtE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IACrF,OAAO,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;QACjF,CAAC,CAAE,OAAoB;QACvB,CAAC,CAAC,SAAS,CAAC;AACd,CAAC;AAQD;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,GAAU;IACzC,MAAM,QAAQ,GAAuB,EAAE,CAAC;IACxC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IAExC,OAAO;QACN,QAAQ;QACR,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE;YAChC,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;YACzE,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACnE,8EAA8E;YAC9E,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAC;YACxC,KAAK,MAAM,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;gBACvD,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;gBAC5B,IAAI,OAAO,MAAM,KAAK,QAAQ;oBAAE,OAAO,KAAK,CAAC;gBAC7C,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;gBAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;YACrC,CAAC;YACD,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;QACnE,CAAC;KACD,CAAC;AACH,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,gBAAgB,CAAC,QAA4B;IAC5D,OAAO;QACN,yFAAyF;QACzF,GAAG,QAAQ,CAAC,GAAG,CACd,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,QAAQ,QAAQ,MAAM,EAAE,CAClF;KACD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACd,CAAC","sourcesContent":["/** One choice the model offers for a question. */\nexport interface QuestionOption {\n\tlabel: string;\n\tdescription: string;\n}\n\n/** A decision the model leaves to the user, with the choices it sees. */\nexport interface UserQuestion {\n\tquestion: string;\n\t/** Two to four. The user may always answer in their own words instead. */\n\toptions: QuestionOption[];\n\t/** Whether more than one option may be chosen. */\n\tmultiSelect: boolean;\n}\n\n/**\n * Answers the questions a model-backed agent asks, one answer per question and in order: the\n * label of each option chosen, or the user's own words. A throw ends the run as an\n * `UnrecoverableError`, as a failing approver does.\n */\nexport type Asker = (\n\tquestions: UserQuestion[],\n\tsignal: AbortSignal,\n) => string[] | Promise<string[]>;\n\n/**\n * The asker's answers, one non-empty string per question, or nothing: an asker is host code, so\n * an answer of any other shape is its bug, not the model's.\n */\nexport function readAnswers(answers: unknown, questions: UserQuestion[]): string[] | undefined {\n\tif (!Array.isArray(answers) || answers.length !== questions.length) return undefined;\n\treturn answers.every(answer => typeof answer === 'string' && answer.trim() !== '')\n\t\t? (answers as string[])\n\t\t: undefined;\n}\n\n/** What one run's user answered, as `rememberAnswers` keeps it. */\nexport interface AnsweredQuestion {\n\tquestion: string;\n\tanswer: string;\n}\n\n/**\n * Wraps `ask` so a question asked again with the same words gets the answer the user already\n * gave, and only the others are asked. `answered` holds every answer, in the order given. The\n * memory lives as long as the returned asker: one per run, so a retry never asks again.\n */\nexport function rememberAnswers(ask: Asker): { ask: Asker; answered: AnsweredQuestion[] } {\n\tconst answered: AnsweredQuestion[] = [];\n\tconst known = new Map<string, string>();\n\n\treturn {\n\t\tanswered,\n\t\task: async (questions, signal) => {\n\t\t\tconst unknown = questions.filter(({ question }) => !known.has(question));\n\t\t\tconst fresh = unknown.length > 0 ? await ask(unknown, signal) : [];\n\t\t\t// The agent checks the asker's answers; a short or odd list is its to refuse.\n\t\t\tif (!Array.isArray(fresh)) return fresh;\n\t\t\tfor (const [index, { question }] of unknown.entries()) {\n\t\t\t\tconst answer = fresh[index];\n\t\t\t\tif (typeof answer !== 'string') return fresh;\n\t\t\t\tknown.set(question, answer);\n\t\t\t\tanswered.push({ question, answer });\n\t\t\t}\n\t\t\treturn questions.map(({ question }) => known.get(question) ?? '');\n\t\t},\n\t};\n}\n\n/** The user's earlier answers, for a prompt that must not ask them again. */\nexport function describeAnswered(answered: AnsweredQuestion[]): string {\n\treturn [\n\t\t'The user already answered these questions in an earlier attempt; do not ask them again:',\n\t\t...answered.map(\n\t\t\t({ question, answer }, index) => `${String(index + 1)}. ${question}\\n ${answer}`,\n\t\t),\n\t].join('\\n');\n}\n"]}
|
|
@@ -29,3 +29,20 @@ export declare class RunContext {
|
|
|
29
29
|
export declare function runContextOf(options: RunOptions): RunContext | undefined;
|
|
30
30
|
/** The same options, carrying `context` for every agent the run reaches. */
|
|
31
31
|
export declare function withRunContext(options: RunOptions, context: RunContext): RunOptions;
|
|
32
|
+
/**
|
|
33
|
+
* Where an agent another agent's tool started runs: the call that started it, and how many
|
|
34
|
+
* delegations deep it is, 1 for an agent the top-level agent started.
|
|
35
|
+
*/
|
|
36
|
+
export interface Delegation {
|
|
37
|
+
/** The name of the tool that started it, such as `explore` or a specialist's. */
|
|
38
|
+
readonly tool: string;
|
|
39
|
+
/** The `id` of that call's `tool` events. */
|
|
40
|
+
readonly callId: string;
|
|
41
|
+
/** Which of the agents that call started, from 1. */
|
|
42
|
+
readonly subagent: number;
|
|
43
|
+
readonly depth: number;
|
|
44
|
+
}
|
|
45
|
+
/** The call that started the agent these options reach, if another agent's tool did. */
|
|
46
|
+
export declare function delegationOf(options: RunOptions): Delegation | undefined;
|
|
47
|
+
/** The same options, for an agent `delegation` started. */
|
|
48
|
+
export declare function withDelegation(options: RunOptions, delegation: Delegation): RunOptions;
|
|
@@ -44,4 +44,13 @@ export function runContextOf(options) {
|
|
|
44
44
|
export function withRunContext(options, context) {
|
|
45
45
|
return { ...options, [runContextKey]: context };
|
|
46
46
|
}
|
|
47
|
+
const delegationKey = Symbol('mikode-harness.delegation');
|
|
48
|
+
/** The call that started the agent these options reach, if another agent's tool did. */
|
|
49
|
+
export function delegationOf(options) {
|
|
50
|
+
return options[delegationKey];
|
|
51
|
+
}
|
|
52
|
+
/** The same options, for an agent `delegation` started. */
|
|
53
|
+
export function withDelegation(options, delegation) {
|
|
54
|
+
return { ...options, [delegationKey]: delegation };
|
|
55
|
+
}
|
|
47
56
|
//# sourceMappingURL=runContext.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runContext.js","sourceRoot":"","sources":["../../../src/agent/domain/runContext.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH,MAAM,OAAO,UAAU;IACL,SAAS,GAAuC,EAAE,CAAC;IAEpE;;;OAGG;IACM,UAAU,GAAuB,EAAE,CAAC;IAE7C,yDAAyD;IACzD,QAAQ,CAAC,QAAwC;QAChD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,MAAM,CAAC,GAAW;QACvB,MAAM,QAAQ,GAAc,EAAE,CAAC;QAC/B,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YACjD,IAAI,CAAC;gBACJ,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;YACrB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;QACF,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC5C,CAAC;CACD;AAED,MAAM,aAAa,GAAG,MAAM,CAAC,2BAA2B,CAAC,CAAC;AAM1D,qFAAqF;AACrF,MAAM,UAAU,YAAY,CAAC,OAAmB;IAC/C,OAAQ,OAAgC,CAAC,aAAa,CAAC,CAAC;AACzD,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,cAAc,CAAC,OAAmB,EAAE,OAAmB;IACtE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC,aAAa,CAAC,EAAE,OAAO,EAA0B,CAAC;AACzE,CAAC","sourcesContent":["import type { RunOptions } from './agent.ts';\n\nexport type RunEnd = 'completed' | 'failed' | 'cancelled';\n\n/**\n * What one top-level run shares with every agent and tool inside it, across all its roles,\n * rounds and retries, such as the record of what it wrote. Whatever a tool starts for the run\n * registers how to end it, and the agent that created the context ends it with the run.\n *\n * Internal: it travels in `RunOptions` under a symbol, so decorators, which pass the options on\n * whole, carry it without knowing it exists, and no consumer can see or set it.\n */\nexport class RunContext {\n\tprivate readonly finishers: ((end: RunEnd) => Promise<void>)[] = [];\n\n\t/**\n\t * The run the workspace's history recorded for this run, once it first wrote. The object is\n\t * shared, not copied: a turn recorded before the first write still learns its run.\n\t */\n\treadonly historyRun: { runId?: string } = {};\n\n\t/** Ends something the run started, when the run ends. */\n\tonFinish(finisher: (end: RunEnd) => Promise<void>): void {\n\t\tthis.finishers.push(finisher);\n\t}\n\n\t/**\n\t * Ends everything registered, all of it even when one fails, and then throws the first\n\t * failure.\n\t */\n\tasync finish(end: RunEnd): Promise<void> {\n\t\tconst failures: unknown[] = [];\n\t\tfor (const finisher of this.finishers.splice(0)) {\n\t\t\ttry {\n\t\t\t\tawait finisher(end);\n\t\t\t} catch (error) {\n\t\t\t\tfailures.push(error);\n\t\t\t}\n\t\t}\n\t\tif (failures.length > 0) throw failures[0];\n\t}\n}\n\nconst runContextKey = Symbol('mikode-harness.runContext');\n\ninterface ContextualRunOptions extends RunOptions {\n\t[runContextKey]?: RunContext;\n}\n\n/** The context of the run these options belong to, if an outer agent created one. */\nexport function runContextOf(options: RunOptions): RunContext | undefined {\n\treturn (options as ContextualRunOptions)[runContextKey];\n}\n\n/** The same options, carrying `context` for every agent the run reaches. */\nexport function withRunContext(options: RunOptions, context: RunContext): RunOptions {\n\treturn { ...options, [runContextKey]: context } as ContextualRunOptions;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"runContext.js","sourceRoot":"","sources":["../../../src/agent/domain/runContext.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH,MAAM,OAAO,UAAU;IACL,SAAS,GAAuC,EAAE,CAAC;IAEpE;;;OAGG;IACM,UAAU,GAAuB,EAAE,CAAC;IAE7C,yDAAyD;IACzD,QAAQ,CAAC,QAAwC;QAChD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,MAAM,CAAC,GAAW;QACvB,MAAM,QAAQ,GAAc,EAAE,CAAC;QAC/B,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YACjD,IAAI,CAAC;gBACJ,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;YACrB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;QACF,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC5C,CAAC;CACD;AAED,MAAM,aAAa,GAAG,MAAM,CAAC,2BAA2B,CAAC,CAAC;AAM1D,qFAAqF;AACrF,MAAM,UAAU,YAAY,CAAC,OAAmB;IAC/C,OAAQ,OAAgC,CAAC,aAAa,CAAC,CAAC;AACzD,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,cAAc,CAAC,OAAmB,EAAE,OAAmB;IACtE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC,aAAa,CAAC,EAAE,OAAO,EAA0B,CAAC;AACzE,CAAC;AAgBD,MAAM,aAAa,GAAG,MAAM,CAAC,2BAA2B,CAAC,CAAC;AAM1D,wFAAwF;AACxF,MAAM,UAAU,YAAY,CAAC,OAAmB;IAC/C,OAAQ,OAA+B,CAAC,aAAa,CAAC,CAAC;AACxD,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,cAAc,CAAC,OAAmB,EAAE,UAAsB;IACzE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC,aAAa,CAAC,EAAE,UAAU,EAAyB,CAAC;AAC3E,CAAC","sourcesContent":["import type { RunOptions } from './agent.ts';\n\nexport type RunEnd = 'completed' | 'failed' | 'cancelled';\n\n/**\n * What one top-level run shares with every agent and tool inside it, across all its roles,\n * rounds and retries, such as the record of what it wrote. Whatever a tool starts for the run\n * registers how to end it, and the agent that created the context ends it with the run.\n *\n * Internal: it travels in `RunOptions` under a symbol, so decorators, which pass the options on\n * whole, carry it without knowing it exists, and no consumer can see or set it.\n */\nexport class RunContext {\n\tprivate readonly finishers: ((end: RunEnd) => Promise<void>)[] = [];\n\n\t/**\n\t * The run the workspace's history recorded for this run, once it first wrote. The object is\n\t * shared, not copied: a turn recorded before the first write still learns its run.\n\t */\n\treadonly historyRun: { runId?: string } = {};\n\n\t/** Ends something the run started, when the run ends. */\n\tonFinish(finisher: (end: RunEnd) => Promise<void>): void {\n\t\tthis.finishers.push(finisher);\n\t}\n\n\t/**\n\t * Ends everything registered, all of it even when one fails, and then throws the first\n\t * failure.\n\t */\n\tasync finish(end: RunEnd): Promise<void> {\n\t\tconst failures: unknown[] = [];\n\t\tfor (const finisher of this.finishers.splice(0)) {\n\t\t\ttry {\n\t\t\t\tawait finisher(end);\n\t\t\t} catch (error) {\n\t\t\t\tfailures.push(error);\n\t\t\t}\n\t\t}\n\t\tif (failures.length > 0) throw failures[0];\n\t}\n}\n\nconst runContextKey = Symbol('mikode-harness.runContext');\n\ninterface ContextualRunOptions extends RunOptions {\n\t[runContextKey]?: RunContext;\n}\n\n/** The context of the run these options belong to, if an outer agent created one. */\nexport function runContextOf(options: RunOptions): RunContext | undefined {\n\treturn (options as ContextualRunOptions)[runContextKey];\n}\n\n/** The same options, carrying `context` for every agent the run reaches. */\nexport function withRunContext(options: RunOptions, context: RunContext): RunOptions {\n\treturn { ...options, [runContextKey]: context } as ContextualRunOptions;\n}\n\n/**\n * Where an agent another agent's tool started runs: the call that started it, and how many\n * delegations deep it is, 1 for an agent the top-level agent started.\n */\nexport interface Delegation {\n\t/** The name of the tool that started it, such as `explore` or a specialist's. */\n\treadonly tool: string;\n\t/** The `id` of that call's `tool` events. */\n\treadonly callId: string;\n\t/** Which of the agents that call started, from 1. */\n\treadonly subagent: number;\n\treadonly depth: number;\n}\n\nconst delegationKey = Symbol('mikode-harness.delegation');\n\ninterface DelegatedRunOptions extends RunOptions {\n\t[delegationKey]?: Delegation;\n}\n\n/** The call that started the agent these options reach, if another agent's tool did. */\nexport function delegationOf(options: RunOptions): Delegation | undefined {\n\treturn (options as DelegatedRunOptions)[delegationKey];\n}\n\n/** The same options, for an agent `delegation` started. */\nexport function withDelegation(options: RunOptions, delegation: Delegation): RunOptions {\n\treturn { ...options, [delegationKey]: delegation } as DelegatedRunOptions;\n}\n"]}
|
|
@@ -9,6 +9,16 @@ export interface Transcript {
|
|
|
9
9
|
* sent. Missing for an agent from `createLLMAgent`.
|
|
10
10
|
*/
|
|
11
11
|
agent?: ProgressAgent | 'explorer';
|
|
12
|
+
/**
|
|
13
|
+
* For an agent another agent's tool started, such as an explorer or the agent of a tool from
|
|
14
|
+
* `agentAsTool`, the call that started it: the tool's name, the `id` of that call, which is
|
|
15
|
+
* in the transcript of the agent that made it, and which of the call's agents it was, from 1.
|
|
16
|
+
*/
|
|
17
|
+
delegation?: {
|
|
18
|
+
tool: string;
|
|
19
|
+
callId: string;
|
|
20
|
+
subagent: number;
|
|
21
|
+
};
|
|
12
22
|
/** How the run ended. A failed run may be retried, and each attempt has its own transcript. */
|
|
13
23
|
outcome: 'completed' | 'failed' | 'cancelled';
|
|
14
24
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transcript.js","sourceRoot":"","sources":["../../../src/agent/domain/transcript.ts"],"names":[],"mappings":"","sourcesContent":["import type { ProgressAgent } from './agent.ts';\n\n/**\n * What the model of one run saw, for a person debugging a run that went wrong. Only agents on\n * the model APIs produce one.\n */\nexport interface Transcript {\n\t/**\n\t * In `createLLMOrchestrator`, the role the run was of, or `explorer` for an explorer a role\n\t * sent. Missing for an agent from `createLLMAgent`.\n\t */\n\tagent?: ProgressAgent | 'explorer';\n\t/** How the run ended. A failed run may be retried, and each attempt has its own transcript. */\n\toutcome: 'completed' | 'failed' | 'cancelled';\n\t/**\n\t * Plain JSON for a person to read: the instructions, the tool definitions, every message of\n\t * the run's context with the model's last answer and what the tools returned, and the error\n\t * a run that did not complete ended with. Unstable: its shape can change in any release, so\n\t * read it, but do not build on it.\n\t */\n\tcontent: unknown;\n}\n\n/**\n * Receives each run's transcript once the run ended, however it ended. A failure here is\n * logged, never the run's: debugging must not change what it observes.\n */\nexport type TranscriptCallback = (transcript: Transcript) => void | Promise<void>;\n"]}
|
|
1
|
+
{"version":3,"file":"transcript.js","sourceRoot":"","sources":["../../../src/agent/domain/transcript.ts"],"names":[],"mappings":"","sourcesContent":["import type { ProgressAgent } from './agent.ts';\n\n/**\n * What the model of one run saw, for a person debugging a run that went wrong. Only agents on\n * the model APIs produce one.\n */\nexport interface Transcript {\n\t/**\n\t * In `createLLMOrchestrator`, the role the run was of, or `explorer` for an explorer a role\n\t * sent. Missing for an agent from `createLLMAgent`.\n\t */\n\tagent?: ProgressAgent | 'explorer';\n\t/**\n\t * For an agent another agent's tool started, such as an explorer or the agent of a tool from\n\t * `agentAsTool`, the call that started it: the tool's name, the `id` of that call, which is\n\t * in the transcript of the agent that made it, and which of the call's agents it was, from 1.\n\t */\n\tdelegation?: { tool: string; callId: string; subagent: number };\n\t/** How the run ended. A failed run may be retried, and each attempt has its own transcript. */\n\toutcome: 'completed' | 'failed' | 'cancelled';\n\t/**\n\t * Plain JSON for a person to read: the instructions, the tool definitions, every message of\n\t * the run's context with the model's last answer and what the tools returned, and the error\n\t * a run that did not complete ended with. Unstable: its shape can change in any release, so\n\t * read it, but do not build on it.\n\t */\n\tcontent: unknown;\n}\n\n/**\n * Receives each run's transcript once the run ended, however it ended. A failure here is\n * logged, never the run's: debugging must not change what it observes.\n */\nexport type TranscriptCallback = (transcript: Transcript) => void | Promise<void>;\n"]}
|
|
@@ -4,11 +4,6 @@ export declare const askUserName = "askUser";
|
|
|
4
4
|
export declare const askUserDefinition: ToolDefinition;
|
|
5
5
|
/** The questions in a call's input, or why they cannot be asked, which the model reads. */
|
|
6
6
|
export declare function readQuestions(input: unknown): UserQuestion[] | string;
|
|
7
|
-
/**
|
|
8
|
-
* The asker's answers, one non-empty string per question, or nothing: an asker is host code, so
|
|
9
|
-
* an answer of any other shape is its bug, not the model's.
|
|
10
|
-
*/
|
|
11
|
-
export declare function readAnswers(answers: unknown, questions: UserQuestion[]): string[] | undefined;
|
|
12
7
|
/** What the model reads back: each question with its answer. */
|
|
13
8
|
export declare function describeAnswers(questions: UserQuestion[], answers: string[]): string;
|
|
14
9
|
export declare function summarizeQuestions(questions: UserQuestion[]): string;
|
|
@@ -79,15 +79,6 @@ export function readQuestions(input) {
|
|
|
79
79
|
}
|
|
80
80
|
return read;
|
|
81
81
|
}
|
|
82
|
-
/**
|
|
83
|
-
* The asker's answers, one non-empty string per question, or nothing: an asker is host code, so
|
|
84
|
-
* an answer of any other shape is its bug, not the model's.
|
|
85
|
-
*/
|
|
86
|
-
export function readAnswers(answers, questions) {
|
|
87
|
-
if (!Array.isArray(answers) || answers.length !== questions.length)
|
|
88
|
-
return undefined;
|
|
89
|
-
return answers.every(isText) ? answers : undefined;
|
|
90
|
-
}
|
|
91
82
|
/** What the model reads back: each question with its answer. */
|
|
92
83
|
export function describeAnswers(questions, answers) {
|
|
93
84
|
return [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"askUser.js","sourceRoot":"","sources":["../../../../src/engines/domain/model/askUser.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"askUser.js","sourceRoot":"","sources":["../../../../src/engines/domain/model/askUser.ts"],"names":[],"mappings":"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","sourcesContent":["import type { UserQuestion } from '#src/agent/domain/question';\nimport type { ToolDefinition } from '#src/llm/domain/tool';\n\nexport const askUserName = 'askUser';\n\nconst maxQuestions = 4;\nconst minOptions = 2;\nconst maxOptions = 4;\n\n// Strict mode allows no length bounds, so the counts live in the description and in `readQuestions`.\nexport const askUserDefinition: ToolDefinition = {\n\tname: askUserName,\n\tdescription: [\n\t\t'Asks the user to decide something that is theirs to decide, and waits for the answer.',\n\t\t'Ask before you build on a choice, not after. The user decides: the shape of a public API or of anything other code depends on; a new dependency; behaviour the request does not ask for; and any choice between options with real trade-offs.',\n\t\t'Do not ask what the repository, the request or a reasonable default already answers; find it out instead.',\n\t\t`Ask everything you need in one call: 1 to ${String(maxQuestions)} questions, each with ${String(minOptions)} to ${String(maxOptions)} options. Say in each option's description what choosing it means. The user may answer in their own words instead.`,\n\t].join(' '),\n\tinputSchema: {\n\t\ttype: 'object',\n\t\tproperties: {\n\t\t\tquestions: {\n\t\t\t\ttype: 'array',\n\t\t\t\tdescription: `1 to ${String(maxQuestions)} questions.`,\n\t\t\t\titems: {\n\t\t\t\t\ttype: 'object',\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tquestion: { type: 'string', description: 'The whole question, ending with \"?\".' },\n\t\t\t\t\t\toptions: {\n\t\t\t\t\t\t\ttype: 'array',\n\t\t\t\t\t\t\tdescription: `${String(minOptions)} to ${String(maxOptions)} choices.`,\n\t\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\t\ttype: 'object',\n\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\tlabel: { type: 'string', description: 'A few words.' },\n\t\t\t\t\t\t\t\t\tdescription: {\n\t\t\t\t\t\t\t\t\t\ttype: 'string',\n\t\t\t\t\t\t\t\t\t\tdescription: 'What choosing it means, and what it costs.',\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\trequired: ['label', 'description'],\n\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmultiSelect: {\n\t\t\t\t\t\t\ttype: 'boolean',\n\t\t\t\t\t\t\tdescription: 'Whether more than one option may be chosen.',\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\trequired: ['question', 'options', 'multiSelect'],\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\trequired: ['questions'],\n\t\tadditionalProperties: false,\n\t},\n};\n\n/** The questions in a call's input, or why they cannot be asked, which the model reads. */\nexport function readQuestions(input: unknown): UserQuestion[] | string {\n\tconst questions = isRecord(input) ? input.questions : undefined;\n\tif (!Array.isArray(questions) || questions.length === 0 || questions.length > maxQuestions) {\n\t\treturn `Ask 1 to ${String(maxQuestions)} questions.`;\n\t}\n\tconst read: UserQuestion[] = [];\n\tfor (const [index, question] of questions.entries()) {\n\t\tconst at = `Question ${String(index + 1)}`;\n\t\tif (!isRecord(question) || !isText(question.question)) return `${at} has no text.`;\n\t\tconst { options, multiSelect } = question;\n\t\tif (!Array.isArray(options) || options.length < minOptions || options.length > maxOptions) {\n\t\t\treturn `${at} needs ${String(minOptions)} to ${String(maxOptions)} options.`;\n\t\t}\n\t\tif (typeof multiSelect !== 'boolean') return `${at} must say whether it is multiSelect.`;\n\t\tconst choices = [];\n\t\tfor (const option of options) {\n\t\t\tif (!isRecord(option) || !isText(option.label) || typeof option.description !== 'string') {\n\t\t\t\treturn `Every option of ${at.toLowerCase()} needs a label and a description.`;\n\t\t\t}\n\t\t\tchoices.push({ label: option.label, description: option.description });\n\t\t}\n\t\tread.push({ question: question.question, options: choices, multiSelect });\n\t}\n\treturn read;\n}\n\n/** What the model reads back: each question with its answer. */\nexport function describeAnswers(questions: UserQuestion[], answers: string[]): string {\n\treturn [\n\t\t'The user answered:',\n\t\t...questions.map(\n\t\t\t({ question }, index) => `${String(index + 1)}. ${question}\\n ${answers[index] ?? ''}`,\n\t\t),\n\t].join('\\n');\n}\n\nexport function summarizeQuestions(questions: UserQuestion[]): string {\n\treturn questions.length === 1 ? '1 question' : `${String(questions.length)} questions`;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isText(value: unknown): value is string {\n\treturn typeof value === 'string' && value.trim() !== '';\n}\n"]}
|
|
@@ -67,8 +67,8 @@ export declare class LLMAgent implements Agent {
|
|
|
67
67
|
private denial;
|
|
68
68
|
/**
|
|
69
69
|
* Always answers the call: a missing tool, a denied call or a failing one becomes an error
|
|
70
|
-
* result, so the model can correct itself. Only a cancellation,
|
|
71
|
-
*
|
|
70
|
+
* result, so the model can correct itself. Only a cancellation, a failing approver, or host
|
|
71
|
+
* code that failed inside the call, such as an agent's approver, escapes.
|
|
72
72
|
*/
|
|
73
73
|
private runTool;
|
|
74
74
|
/**
|
|
@@ -76,8 +76,24 @@ export declare class LLMAgent implements Agent {
|
|
|
76
76
|
* correct; an asker that fails or answers wrongly is host code, and ends the run.
|
|
77
77
|
*/
|
|
78
78
|
private askUser;
|
|
79
|
-
/**
|
|
79
|
+
/**
|
|
80
|
+
* One at a time and in order, so their effects and their narration never interleave. Only
|
|
81
|
+
* calls of a `parallel` tool next to each other run at once, such as agents being delegated
|
|
82
|
+
* to, each narrated as it goes.
|
|
83
|
+
*/
|
|
80
84
|
private runTools;
|
|
85
|
+
/** The calls from `index` on whose tool is `parallel`, up to the first that is not. */
|
|
86
|
+
private parallelFrom;
|
|
87
|
+
/**
|
|
88
|
+
* Runs the calls at once, their results in the order of the calls. The first failure that
|
|
89
|
+
* ends the run stops the others, and the run ends once every one has.
|
|
90
|
+
*/
|
|
91
|
+
private runTogether;
|
|
92
|
+
/**
|
|
93
|
+
* Narrates how a call ended and notes its command's ending, with the warning for the model
|
|
94
|
+
* when that ending keeps coming back alike.
|
|
95
|
+
*/
|
|
96
|
+
private settle;
|
|
81
97
|
/**
|
|
82
98
|
* On a loop found, the fifth ending alike of a command or the fifth answer in a row asking for
|
|
83
99
|
* the same calls: the note for the model if the user lets the run go on, or the failure that
|