Merge remote-tracking branch 'origin/master' into fs-overwrite-diff-bound-v2
# Conflicts: # docs/subsystems/lsp.i18n.yaml # packages/fs/fs-local/README.i18n.yaml # packages/fs/fs-local/README.md # packages/fs/fs-local/README.zh.md # packages/fs/fs-local/src/index.ts
This commit is contained in:
@@ -352,9 +352,10 @@ const SCENARIOS: Scenario[] = [
|
||||
},
|
||||
// Authored durable-catalog transcript: the snapshot-only lifecycle marker
|
||||
// fences the second parent turn behind the child's Activation end, so
|
||||
// `list_agents` deterministically reads the persisted child as complete.
|
||||
// The tool itself executes for real against the control service, session
|
||||
// query, and JSONL persistence; the marker is not model-visible.
|
||||
// `list_agents({ scope: 'descendants' })` deterministically reads the
|
||||
// persisted child as complete, then `interrupt_agent` executes its accepted
|
||||
// no-op against that settled id. Both tools run through the assembled control
|
||||
// service; the marker is not model-visible.
|
||||
{
|
||||
name: 'subagent-list-agents',
|
||||
hasModelTurn: true,
|
||||
|
||||
@@ -94,8 +94,16 @@ interface ToolArgsMap {
|
||||
} & Record<string, JsonValue>;
|
||||
/** Read the current same-session goal, including its exact id/revision, objective, phase, completed continuation rounds, round limit, blocker reason when present, and whether another continuation is armed. Call this before updating a goal. */
|
||||
get_goal: Record<string, JsonValue>;
|
||||
/** List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. */
|
||||
list_agents: Record<string, JsonValue>;
|
||||
/** Request cancellation of a background agent's current turn by its agent id. The target may be your direct child or a deeper agent created under you. Only the current turn stops: messages already queued for the agent stay parked until a later send_message, agents it started keep running, and the agent itself stays available for follow-ups. This call returns as soon as the stop request is accepted, so the target may keep running briefly; interrupting an agent that already finished is an accepted no-op. */
|
||||
interrupt_agent: {
|
||||
/** The agent id of the running agent to interrupt. */
|
||||
agent_id: string;
|
||||
} & Record<string, JsonValue>;
|
||||
/** List your continuable background subagents by durable id and label. Status comes from the live registry: running means the agent is working right now, idle means it is loaded but between turns (it may be waiting on agents it started), and complete means it exists only in storage — a direct child remains a `send_message` candidate in every status. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. Scope `descendants` walks the whole tree below you in stable pre-order, annotating each entry with its durable direct-parent session id and depth. You may use `send_message` only for depth-1 entries; deeper entries are candidates for `interrupt_agent` only. */
|
||||
list_agents: {
|
||||
/** children (default) lists direct children only; descendants walks the complete tree below you. */
|
||||
scope?: "children" | "descendants";
|
||||
} & Record<string, JsonValue>;
|
||||
/** Run a foreground fresh-agent Ralph loop toward one immutable objective. Use only when the direct human explicitly asks for Ralph or fresh-agent iteration. Each round opens a new child with no parent conversation or prior child session; the shared workspace is long-term memory, and only a bounded structured report crosses rounds. The call returns when a worker reports completion or a concrete blocker, or at the round limit. Ordinary long-running same-session work belongs to goal tools. */
|
||||
ralph: {
|
||||
/** The immutable completion objective for every fresh Ralph round. */
|
||||
@@ -304,15 +312,22 @@ interface ToolOutputMap {
|
||||
};
|
||||
activation: "armed" | "disarmed";
|
||||
};
|
||||
interrupt_agent: {
|
||||
accepted: boolean;
|
||||
};
|
||||
list_agents: ({
|
||||
kind: "child";
|
||||
id: string;
|
||||
label: string;
|
||||
status: "running" | "complete";
|
||||
status: "running" | "idle" | "complete";
|
||||
parent?: string;
|
||||
depth?: number;
|
||||
} | {
|
||||
kind: "diagnostic";
|
||||
id: string;
|
||||
reason: "corrupt" | "unsupported" | "unavailable";
|
||||
parent?: string;
|
||||
depth?: number;
|
||||
})[];
|
||||
ralph: {
|
||||
runId: string;
|
||||
|
||||
@@ -173,11 +173,36 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
|
||||
"name": "interrupt_agent",
|
||||
"description": "Request cancellation of a background agent's current turn by its agent id. The target may be your direct child or a deeper agent created under you. Only the current turn stops: messages already queued for the agent stay parked until a later send_message, agents it started keep running, and the agent itself stays available for follow-ups. This call returns as soon as the stop request is accepted, so the target may keep running briefly; interrupting an agent that already finished is an accepted no-op.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
"properties": {
|
||||
"agent_id": {
|
||||
"type": "string",
|
||||
"description": "The agent id of the running agent to interrupt."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agent_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status comes from the live registry: running means the agent is working right now, idle means it is loaded but between turns (it may be waiting on agents it started), and complete means it exists only in storage — a direct child remains a `send_message` candidate in every status. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. Scope `descendants` walks the whole tree below you in stable pre-order, annotating each entry with its durable direct-parent session id and depth. You may use `send_message` only for depth-1 entries; deeper entries are candidates for `interrupt_agent` only.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"description": "children (default) lists direct children only; descendants walks the complete tree below you.",
|
||||
"enum": [
|
||||
"children",
|
||||
"descendants"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -116,11 +116,36 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
|
||||
"name": "interrupt_agent",
|
||||
"description": "Request cancellation of a background agent's current turn by its agent id. The target may be your direct child or a deeper agent created under you. Only the current turn stops: messages already queued for the agent stay parked until a later send_message, agents it started keep running, and the agent itself stays available for follow-ups. This call returns as soon as the stop request is accepted, so the target may keep running briefly; interrupting an agent that already finished is an accepted no-op.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
"properties": {
|
||||
"agent_id": {
|
||||
"type": "string",
|
||||
"description": "The agent id of the running agent to interrupt."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agent_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status comes from the live registry: running means the agent is working right now, idle means it is loaded but between turns (it may be waiting on agents it started), and complete means it exists only in storage — a direct child remains a `send_message` candidate in every status. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. Scope `descendants` walks the whole tree below you in stable pre-order, annotating each entry with its durable direct-parent session id and depth. You may use `send_message` only for depth-1 entries; deeper entries are candidates for `interrupt_agent` only.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"description": "children (default) lists direct children only; descendants walks the complete tree below you.",
|
||||
"enum": [
|
||||
"children",
|
||||
"descendants"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -77,8 +77,16 @@ interface ToolArgsMap {
|
||||
} & Record<string, JsonValue>;
|
||||
/** Read the current same-session goal, including its exact id/revision, objective, phase, completed continuation rounds, round limit, blocker reason when present, and whether another continuation is armed. Call this before updating a goal. */
|
||||
get_goal: Record<string, JsonValue>;
|
||||
/** List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. */
|
||||
list_agents: Record<string, JsonValue>;
|
||||
/** Request cancellation of a background agent's current turn by its agent id. The target may be your direct child or a deeper agent created under you. Only the current turn stops: messages already queued for the agent stay parked until a later send_message, agents it started keep running, and the agent itself stays available for follow-ups. This call returns as soon as the stop request is accepted, so the target may keep running briefly; interrupting an agent that already finished is an accepted no-op. */
|
||||
interrupt_agent: {
|
||||
/** The agent id of the running agent to interrupt. */
|
||||
agent_id: string;
|
||||
} & Record<string, JsonValue>;
|
||||
/** List your continuable background subagents by durable id and label. Status comes from the live registry: running means the agent is working right now, idle means it is loaded but between turns (it may be waiting on agents it started), and complete means it exists only in storage — a direct child remains a `send_message` candidate in every status. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. Scope `descendants` walks the whole tree below you in stable pre-order, annotating each entry with its durable direct-parent session id and depth. You may use `send_message` only for depth-1 entries; deeper entries are candidates for `interrupt_agent` only. */
|
||||
list_agents: {
|
||||
/** children (default) lists direct children only; descendants walks the complete tree below you. */
|
||||
scope?: "children" | "descendants";
|
||||
} & Record<string, JsonValue>;
|
||||
/** Run a foreground fresh-agent Ralph loop toward one immutable objective. Use only when the direct human explicitly asks for Ralph or fresh-agent iteration. Each round opens a new child with no parent conversation or prior child session; the shared workspace is long-term memory, and only a bounded structured report crosses rounds. The call returns when a worker reports completion or a concrete blocker, or at the round limit. Ordinary long-running same-session work belongs to goal tools. */
|
||||
ralph: {
|
||||
/** The immutable completion objective for every fresh Ralph round. */
|
||||
@@ -275,15 +283,22 @@ interface ToolOutputMap {
|
||||
};
|
||||
activation: "armed" | "disarmed";
|
||||
};
|
||||
interrupt_agent: {
|
||||
accepted: boolean;
|
||||
};
|
||||
list_agents: ({
|
||||
kind: "child";
|
||||
id: string;
|
||||
label: string;
|
||||
status: "running" | "complete";
|
||||
status: "running" | "idle" | "complete";
|
||||
parent?: string;
|
||||
depth?: number;
|
||||
} | {
|
||||
kind: "diagnostic";
|
||||
id: string;
|
||||
reason: "corrupt" | "unsupported" | "unavailable";
|
||||
parent?: string;
|
||||
depth?: number;
|
||||
})[];
|
||||
ralph: {
|
||||
runId: string;
|
||||
|
||||
@@ -116,11 +116,36 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
|
||||
"name": "interrupt_agent",
|
||||
"description": "Request cancellation of a background agent's current turn by its agent id. The target may be your direct child or a deeper agent created under you. Only the current turn stops: messages already queued for the agent stay parked until a later send_message, agents it started keep running, and the agent itself stays available for follow-ups. This call returns as soon as the stop request is accepted, so the target may keep running briefly; interrupting an agent that already finished is an accepted no-op.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
"properties": {
|
||||
"agent_id": {
|
||||
"type": "string",
|
||||
"description": "The agent id of the running agent to interrupt."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agent_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status comes from the live registry: running means the agent is working right now, idle means it is loaded but between turns (it may be waiting on agents it started), and complete means it exists only in storage — a direct child remains a `send_message` candidate in every status. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. Scope `descendants` walks the whole tree below you in stable pre-order, annotating each entry with its durable direct-parent session id and depth. You may use `send_message` only for depth-1 entries; deeper entries are candidates for `interrupt_agent` only.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"description": "children (default) lists direct children only; descendants walks the complete tree below you.",
|
||||
"enum": [
|
||||
"children",
|
||||
"descendants"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -116,11 +116,36 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
|
||||
"name": "interrupt_agent",
|
||||
"description": "Request cancellation of a background agent's current turn by its agent id. The target may be your direct child or a deeper agent created under you. Only the current turn stops: messages already queued for the agent stay parked until a later send_message, agents it started keep running, and the agent itself stays available for follow-ups. This call returns as soon as the stop request is accepted, so the target may keep running briefly; interrupting an agent that already finished is an accepted no-op.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
"properties": {
|
||||
"agent_id": {
|
||||
"type": "string",
|
||||
"description": "The agent id of the running agent to interrupt."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agent_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status comes from the live registry: running means the agent is working right now, idle means it is loaded but between turns (it may be waiting on agents it started), and complete means it exists only in storage — a direct child remains a `send_message` candidate in every status. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. Scope `descendants` walks the whole tree below you in stable pre-order, annotating each entry with its durable direct-parent session id and depth. You may use `send_message` only for depth-1 entries; deeper entries are candidates for `interrupt_agent` only.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"description": "children (default) lists direct children only; descendants walks the complete tree below you.",
|
||||
"enum": [
|
||||
"children",
|
||||
"descendants"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -116,11 +116,36 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
|
||||
"name": "interrupt_agent",
|
||||
"description": "Request cancellation of a background agent's current turn by its agent id. The target may be your direct child or a deeper agent created under you. Only the current turn stops: messages already queued for the agent stay parked until a later send_message, agents it started keep running, and the agent itself stays available for follow-ups. This call returns as soon as the stop request is accepted, so the target may keep running briefly; interrupting an agent that already finished is an accepted no-op.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
"properties": {
|
||||
"agent_id": {
|
||||
"type": "string",
|
||||
"description": "The agent id of the running agent to interrupt."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agent_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status comes from the live registry: running means the agent is working right now, idle means it is loaded but between turns (it may be waiting on agents it started), and complete means it exists only in storage — a direct child remains a `send_message` candidate in every status. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. Scope `descendants` walks the whole tree below you in stable pre-order, annotating each entry with its durable direct-parent session id and depth. You may use `send_message` only for depth-1 entries; deeper entries are candidates for `interrupt_agent` only.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"description": "children (default) lists direct children only; descendants walks the complete tree below you.",
|
||||
"enum": [
|
||||
"children",
|
||||
"descendants"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
+28
-3
@@ -179,11 +179,36 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
|
||||
"name": "interrupt_agent",
|
||||
"description": "Request cancellation of a background agent's current turn by its agent id. The target may be your direct child or a deeper agent created under you. Only the current turn stops: messages already queued for the agent stay parked until a later send_message, agents it started keep running, and the agent itself stays available for follow-ups. This call returns as soon as the stop request is accepted, so the target may keep running briefly; interrupting an agent that already finished is an accepted no-op.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
"properties": {
|
||||
"agent_id": {
|
||||
"type": "string",
|
||||
"description": "The agent id of the running agent to interrupt."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agent_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status comes from the live registry: running means the agent is working right now, idle means it is loaded but between turns (it may be waiting on agents it started), and complete means it exists only in storage — a direct child remains a `send_message` candidate in every status. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. Scope `descendants` walks the whole tree below you in stable pre-order, annotating each entry with its durable direct-parent session id and depth. You may use `send_message` only for depth-1 entries; deeper entries are candidates for `interrupt_agent` only.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"description": "children (default) lists direct children only; descendants walks the complete tree below you.",
|
||||
"enum": [
|
||||
"children",
|
||||
"descendants"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
+28
-3
@@ -116,11 +116,36 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
|
||||
"name": "interrupt_agent",
|
||||
"description": "Request cancellation of a background agent's current turn by its agent id. The target may be your direct child or a deeper agent created under you. Only the current turn stops: messages already queued for the agent stay parked until a later send_message, agents it started keep running, and the agent itself stays available for follow-ups. This call returns as soon as the stop request is accepted, so the target may keep running briefly; interrupting an agent that already finished is an accepted no-op.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
"properties": {
|
||||
"agent_id": {
|
||||
"type": "string",
|
||||
"description": "The agent id of the running agent to interrupt."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agent_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status comes from the live registry: running means the agent is working right now, idle means it is loaded but between turns (it may be waiting on agents it started), and complete means it exists only in storage — a direct child remains a `send_message` candidate in every status. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. Scope `descendants` walks the whole tree below you in stable pre-order, annotating each entry with its durable direct-parent session id and depth. You may use `send_message` only for depth-1 entries; deeper entries are candidates for `interrupt_agent` only.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"description": "children (default) lists direct children only; descendants walks the complete tree below you.",
|
||||
"enum": [
|
||||
"children",
|
||||
"descendants"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
{
|
||||
"op": "prompt",
|
||||
"text": "Call list_agents once and observe the subagent you started. Then reply with the single word DONE. Do not call any other tool."
|
||||
"text": "Call list_agents once with scope set to descendants and observe the subagent you started. Then call interrupt_agent once with agent_id set to 33333333-3333-4333-8333-333333333333. Then reply with the single word DONE. Do not call any other tool."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -26,26 +26,36 @@
|
||||
{"type":"assistant/message","seq":24,"time":1785730454820,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"STARTED"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"2ee459fa-21f9-48c6-a42e-3c38eca1e4c9"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[19,20,21,22,23],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":25,"time":1785730454821,"data":{"turn":1,"step":2}}
|
||||
{"type":"turn/end","seq":26,"time":1785730454821,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
{"type":"agent/inbox/spliced","seq":27,"time":1785730454857,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Call list_agents once and observe the subagent you started. Then reply with the single word DONE. Do not call any other tool."}],"source":{"kind":"user"},"role":"user","id":"3bcac2a3-e0db-465b-94b4-4e2761236475"}]}}
|
||||
{"type":"agent/inbox/spliced","seq":27,"time":1785730454857,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Call list_agents once with scope set to descendants and observe the subagent you started. Then call interrupt_agent once with agent_id set to 33333333-3333-4333-8333-333333333333. Then reply with the single word DONE. Do not call any other tool."}],"source":{"kind":"user"},"role":"user","id":"3bcac2a3-e0db-465b-94b4-4e2761236475"}]}}
|
||||
{"type":"turn/start","seq":28,"time":1785821412840,"data":{"turn":2}}
|
||||
{"type":"agent/inbox/spliced","seq":29,"time":1785821412840,"data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
|
||||
{"type":"step/start","seq":30,"time":1785730454863,"data":{"turn":2,"step":1}}
|
||||
{"type":"user/message","seq":31,"time":1785730454863,"data":{"content":[{"type":"text","text":"Call list_agents once and observe the subagent you started. Then reply with the single word DONE. Do not call any other tool."}],"source":{"kind":"user"},"role":"user","id":"3bcac2a3-e0db-465b-94b4-4e2761236475"},"surfaceOp":"append"}
|
||||
{"type":"user/message","seq":31,"time":1785730454863,"data":{"content":[{"type":"text","text":"Call list_agents once with scope set to descendants and observe the subagent you started. Then call interrupt_agent once with agent_id set to 33333333-3333-4333-8333-333333333333. Then reply with the single word DONE. Do not call any other tool."}],"source":{"kind":"user"},"role":"user","id":"3bcac2a3-e0db-465b-94b4-4e2761236475"},"surfaceOp":"append"}
|
||||
{"type":"assistant/chunk","seq":32,"time":1785531795686,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","seq":33,"time":1785536135065,"data":{"turn":2,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"call_list","name":"list_agents","argumentsDelta":"{}"}}}
|
||||
{"type":"assistant/chunk","seq":34,"time":1785730454867,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_list","name":"list_agents","arguments":"{}"}}}}
|
||||
{"type":"assistant/chunk","seq":33,"time":1785536135065,"data":{"turn":2,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"call_list","name":"list_agents","argumentsDelta":"{\"scope\":\"descendants\"}"}}}
|
||||
{"type":"assistant/chunk","seq":34,"time":1785730454867,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_list","name":"list_agents","arguments":"{\"scope\":\"descendants\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":35,"time":1785730454867,"data":{"turn":2,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
|
||||
{"type":"assistant/chunk","seq":36,"time":1785730454867,"data":{"turn":2,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":37,"time":1785730454867,"data":{"turn":2,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_list","name":"list_agents","arguments":"{}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"199c7794-d782-4957-b7ed-69d094b9c0ef"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[32,33,34,35,36],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":38,"time":1785730454867,"data":{"turn":2,"step":1,"callId":"call_list","name":"list_agents","arguments":"{}"}}
|
||||
{"type":"tool/result","seq":39,"time":1785730454893,"data":{"turn":2,"step":1,"message":{"source":{"kind":"tool","callId":"call_list"},"content":[{"type":"tool-result","toolCallId":"call_list","content":[{"type":"text","text":"33333333-3333-4333-8333-333333333333 [complete] — Reply with CHILD_OK"}],"isError":false}],"role":"user","id":"61ceb650-3e32-413e-9d7e-b6ec8a351858"}},"sourceEventSeqs":[38],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","seq":37,"time":1785730454867,"data":{"turn":2,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_list","name":"list_agents","arguments":"{\"scope\":\"descendants\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"199c7794-d782-4957-b7ed-69d094b9c0ef"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[32,33,34,35,36],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":38,"time":1785730454867,"data":{"turn":2,"step":1,"callId":"call_list","name":"list_agents","arguments":"{\"scope\":\"descendants\"}"}}
|
||||
{"type":"tool/result","seq":39,"time":1785730454893,"data":{"turn":2,"step":1,"message":{"source":{"kind":"tool","callId":"call_list"},"content":[{"type":"tool-result","toolCallId":"call_list","content":[{"type":"text","text":"33333333-3333-4333-8333-333333333333 [complete] parent=11111111-1111-4111-8111-111111111111 depth=1 — Reply with CHILD_OK"}],"isError":false}],"role":"user","id":"61ceb650-3e32-413e-9d7e-b6ec8a351858"}},"sourceEventSeqs":[38],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":40,"time":1785730454893,"data":{"turn":2,"step":1}}
|
||||
{"type":"step/start","seq":41,"time":1785730454903,"data":{"turn":2,"step":2}}
|
||||
{"type":"assistant/chunk","seq":42,"time":1785531795715,"data":{"turn":2,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":1785536135106,"data":{"turn":2,"step":2,"chunk":{"type":"text-delta","index":0,"text":"DONE"}}}
|
||||
{"type":"assistant/chunk","seq":44,"time":1785730454907,"data":{"turn":2,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DONE"}}}}
|
||||
{"type":"assistant/chunk","seq":45,"time":1785730454907,"data":{"turn":2,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":2}}}}
|
||||
{"type":"assistant/chunk","seq":46,"time":1785730454907,"data":{"turn":2,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":47,"time":1785730454907,"data":{"turn":2,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"17c1a7c5-84f5-493e-adb5-6a65219e6ad6"},"usage":{"inputTokens":10,"outputTokens":2}},"sourceEventSeqs":[42,43,44,45,46],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":48,"time":1785730454908,"data":{"turn":2,"step":2}}
|
||||
{"type":"turn/end","seq":49,"time":1785730454908,"data":{"turn":2,"reason":{"kind":"completed"}}}
|
||||
{"type":"assistant/chunk","seq":42,"time":1785531795715,"data":{"turn":2,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":1785536135106,"data":{"turn":2,"step":2,"chunk":{"type":"tool-call-delta","index":0,"id":"call_interrupt","name":"interrupt_agent","argumentsDelta":"{\"agent_id\":\"33333333-3333-4333-8333-333333333333\"}"}}}
|
||||
{"type":"assistant/chunk","seq":44,"time":1785730454907,"data":{"turn":2,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_interrupt","name":"interrupt_agent","arguments":"{\"agent_id\":\"33333333-3333-4333-8333-333333333333\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":45,"time":1785730454907,"data":{"turn":2,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
|
||||
{"type":"assistant/chunk","seq":46,"time":1785730454907,"data":{"turn":2,"step":2,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":47,"time":1785730454907,"data":{"turn":2,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_interrupt","name":"interrupt_agent","arguments":"{\"agent_id\":\"33333333-3333-4333-8333-333333333333\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"17c1a7c5-84f5-493e-adb5-6a65219e6ad6"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[42,43,44,45,46],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":48,"time":1785730454907,"data":{"turn":2,"step":2,"callId":"call_interrupt","name":"interrupt_agent","arguments":"{\"agent_id\":\"33333333-3333-4333-8333-333333333333\"}"}}
|
||||
{"type":"tool/result","seq":49,"time":1785730454917,"data":{"turn":2,"step":2,"message":{"source":{"kind":"tool","callId":"call_interrupt"},"content":[{"type":"tool-result","toolCallId":"call_interrupt","content":[{"type":"text","text":"interrupt requested for agent 33333333-3333-4333-8333-333333333333"}],"isError":false}],"role":"user","id":"bf93cbe0-1946-4aef-a4ce-431790026c6f"}},"sourceEventSeqs":[48],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":50,"time":1785730454917,"data":{"turn":2,"step":2}}
|
||||
{"type":"step/start","seq":51,"time":1785730454927,"data":{"turn":2,"step":3}}
|
||||
{"type":"assistant/chunk","seq":52,"time":1785531795715,"data":{"turn":2,"step":3,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":53,"time":1785536135106,"data":{"turn":2,"step":3,"chunk":{"type":"text-delta","index":0,"text":"DONE"}}}
|
||||
{"type":"assistant/chunk","seq":54,"time":1785730454907,"data":{"turn":2,"step":3,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DONE"}}}}
|
||||
{"type":"assistant/chunk","seq":55,"time":1785730454907,"data":{"turn":2,"step":3,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":2}}}}
|
||||
{"type":"assistant/chunk","seq":56,"time":1785730454907,"data":{"turn":2,"step":3,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":57,"time":1785730454907,"data":{"turn":2,"step":3,"message":{"role":"assistant","content":[{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"e4c04cd6-9c23-4c99-a5f7-bac6fc141e95"},"usage":{"inputTokens":10,"outputTokens":2}},"sourceEventSeqs":[52,53,54,55,56],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":58,"time":1785730454938,"data":{"turn":2,"step":3}}
|
||||
{"type":"turn/end","seq":59,"time":1785730454938,"data":{"turn":2,"reason":{"kind":"completed"}}}
|
||||
|
||||
+28
-3
@@ -116,11 +116,36 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
|
||||
"name": "interrupt_agent",
|
||||
"description": "Request cancellation of a background agent's current turn by its agent id. The target may be your direct child or a deeper agent created under you. Only the current turn stops: messages already queued for the agent stay parked until a later send_message, agents it started keep running, and the agent itself stays available for follow-ups. This call returns as soon as the stop request is accepted, so the target may keep running briefly; interrupting an agent that already finished is an accepted no-op.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
"properties": {
|
||||
"agent_id": {
|
||||
"type": "string",
|
||||
"description": "The agent id of the running agent to interrupt."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agent_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status comes from the live registry: running means the agent is working right now, idle means it is loaded but between turns (it may be waiting on agents it started), and complete means it exists only in storage — a direct child remains a `send_message` candidate in every status. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. Scope `descendants` walks the whole tree below you in stable pre-order, annotating each entry with its durable direct-parent session id and depth. You may use `send_message` only for depth-1 entries; deeper entries are candidates for `interrupt_agent` only.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"description": "children (default) lists direct children only; descendants walks the complete tree below you.",
|
||||
"enum": [
|
||||
"children",
|
||||
"descendants"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -116,11 +116,36 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
|
||||
"name": "interrupt_agent",
|
||||
"description": "Request cancellation of a background agent's current turn by its agent id. The target may be your direct child or a deeper agent created under you. Only the current turn stops: messages already queued for the agent stay parked until a later send_message, agents it started keep running, and the agent itself stays available for follow-ups. This call returns as soon as the stop request is accepted, so the target may keep running briefly; interrupting an agent that already finished is an accepted no-op.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
"properties": {
|
||||
"agent_id": {
|
||||
"type": "string",
|
||||
"description": "The agent id of the running agent to interrupt."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agent_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status comes from the live registry: running means the agent is working right now, idle means it is loaded but between turns (it may be waiting on agents it started), and complete means it exists only in storage — a direct child remains a `send_message` candidate in every status. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. Scope `descendants` walks the whole tree below you in stable pre-order, annotating each entry with its durable direct-parent session id and depth. You may use `send_message` only for depth-1 entries; deeper entries are candidates for `interrupt_agent` only.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"description": "children (default) lists direct children only; descendants walks the complete tree below you.",
|
||||
"enum": [
|
||||
"children",
|
||||
"descendants"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -116,11 +116,36 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
|
||||
"name": "interrupt_agent",
|
||||
"description": "Request cancellation of a background agent's current turn by its agent id. The target may be your direct child or a deeper agent created under you. Only the current turn stops: messages already queued for the agent stay parked until a later send_message, agents it started keep running, and the agent itself stays available for follow-ups. This call returns as soon as the stop request is accepted, so the target may keep running briefly; interrupting an agent that already finished is an accepted no-op.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
"properties": {
|
||||
"agent_id": {
|
||||
"type": "string",
|
||||
"description": "The agent id of the running agent to interrupt."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agent_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status comes from the live registry: running means the agent is working right now, idle means it is loaded but between turns (it may be waiting on agents it started), and complete means it exists only in storage — a direct child remains a `send_message` candidate in every status. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. Scope `descendants` walks the whole tree below you in stable pre-order, annotating each entry with its durable direct-parent session id and depth. You may use `send_message` only for depth-1 entries; deeper entries are candidates for `interrupt_agent` only.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"description": "children (default) lists direct children only; descendants walks the complete tree below you.",
|
||||
"enum": [
|
||||
"children",
|
||||
"descendants"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -116,11 +116,36 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
|
||||
"name": "interrupt_agent",
|
||||
"description": "Request cancellation of a background agent's current turn by its agent id. The target may be your direct child or a deeper agent created under you. Only the current turn stops: messages already queued for the agent stay parked until a later send_message, agents it started keep running, and the agent itself stays available for follow-ups. This call returns as soon as the stop request is accepted, so the target may keep running briefly; interrupting an agent that already finished is an accepted no-op.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
"properties": {
|
||||
"agent_id": {
|
||||
"type": "string",
|
||||
"description": "The agent id of the running agent to interrupt."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agent_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_agents",
|
||||
"description": "List your continuable background subagents by durable id and label. Status comes from the live registry: running means the agent is working right now, idle means it is loaded but between turns (it may be waiting on agents it started), and complete means it exists only in storage — a direct child remains a `send_message` candidate in every status. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. Scope `descendants` walks the whole tree below you in stable pre-order, annotating each entry with its durable direct-parent session id and depth. You may use `send_message` only for depth-1 entries; deeper entries are candidates for `interrupt_agent` only.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"description": "children (default) lists direct children only; descendants walks the complete tree below you.",
|
||||
"enum": [
|
||||
"children",
|
||||
"descendants"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write examples/headless-agent/README.md
|
||||
README.md: e00f3d2d4fd21a860239f3d3a3e5eb2d7520f14a
|
||||
README.zh.md: 6cd845783b1c112ba73676474b176ec28a4d0b78
|
||||
README.md: 6e80e56dec70c2be341ae5dfbad70e13a5109715
|
||||
README.zh.md: ea8c41b9ae75f0cee3edd78e59408f37c19182d3
|
||||
|
||||
@@ -17,6 +17,16 @@ The product command is [`dsh run`](../../apps/cli/README.md): it accepts one non
|
||||
|
||||
Snapshot suites run this directory's configuration through [`tests/fixtures/headless-driver.ts`](tests/fixtures/headless-driver.ts), an unexported test-only process that emits canonical session events as JSONL before its result record. That stream is test infrastructure, not a supported CLI output format. Child sessions surface only through parent tool events and results.
|
||||
|
||||
## E2B POC overlay
|
||||
|
||||
[`e2b.cordis.yml`](e2b.cordis.yml) replaces the local filesystem and subprocess providers with one shared E2B sandbox while retaining `dsh-bash-local` and the same model-facing tools. Put `E2B_API_KEY` beside `DEEPSEEK_API_KEY` in the gitignored root `.env`, then run the credential-gated live composition, which drives FS, Bash, PTY, and LSP in one sandbox and proves final deletion:
|
||||
|
||||
```sh
|
||||
pnpm exec vitest run --config vitest.e2e.config.ts packages/e2b/e2b/tests/composition.e2e.ts
|
||||
```
|
||||
|
||||
The overlay creates the same absolute cwd inside the sandbox, but it does not upload or mount the host workspace. File and Bash mutations exist only in E2B; Cordis, model calls, agent/session state, session logs, skills, and SDK buffers remain on the host. The composition kills its sandbox on timeout and disposal. It is a provider-composition POC, not a whole-harness migration or a workspace-sync feature.
|
||||
|
||||
## Advanced configuration
|
||||
|
||||
[`advanced.cordis.yml`](advanced.cordis.yml) adds Code Mode and the Cordis tools to the test composition.
|
||||
|
||||
@@ -17,6 +17,16 @@ pnpm run dsh run "fix the failing test in this workspace"
|
||||
|
||||
快照套件通过 [`tests/fixtures/headless-driver.ts`](tests/fixtures/headless-driver.ts) 运行本目录的配置。这个未导出且仅供测试使用的进程会在结果记录之前,以 JSONL 发出规范会话事件。该事件流属于测试基础设施,不是受支持的 CLI(命令行界面)输出格式。子会话只通过父会话的工具事件和结果对外显示。
|
||||
|
||||
## E2B POC overlay
|
||||
|
||||
[`e2b.cordis.yml`](e2b.cordis.yml) 使用一个共享 E2B 沙箱替换本地文件系统与进程管理提供方,同时保留 `dsh-bash-local` 和相同的面向模型工具。请在 git 忽略的根目录 `.env` 中,将 `E2B_API_KEY` 与 `DEEPSEEK_API_KEY` 放在一起,然后运行凭据门控的实机组合测试;它在同一个沙箱中驱动 FS、Bash、PTY 和 LSP,并证明沙箱最终被删除:
|
||||
|
||||
```sh
|
||||
pnpm exec vitest run --config vitest.e2e.config.ts packages/e2b/e2b/tests/composition.e2e.ts
|
||||
```
|
||||
|
||||
该 overlay 会在沙箱中创建拼写相同的绝对 cwd,但不会上传或挂载宿主工作区。文件与 Bash 变更只存在于 E2B;Cordis、模型调用、agent/会话状态、会话日志、skill(技能)和 SDK 缓冲仍在宿主上。该组合会在超时和资源释放时终止其沙箱。它是提供方组合 POC,而不是完整 harness 迁移或工作区同步功能。
|
||||
|
||||
## 高级配置
|
||||
|
||||
[`advanced.cordis.yml`](advanced.cordis.yml) 在测试组装中添加 Code Mode 和 Cordis 工具。
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# POC overlay: keep the advanced headless agent and model-facing tools, but
|
||||
# place its filesystem and process substrate in one short-lived E2B sandbox;
|
||||
# the generic Bash, PTY, and LSP consumers compose above them.
|
||||
#
|
||||
# One-world invariant: e2b.cwd, sandbox-policy.workspaceRoot, and bash-local's
|
||||
# default workdir (implicit host process.cwd()) must all name the same remote
|
||||
# directory. Only e2b.cwd is created at sandbox open; dropping its !!js line
|
||||
# falls back to /home/user/workspace while Bash and PTY keep targeting the
|
||||
# host path, so every tool call fails with a remote spawn error.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
config:
|
||||
path: ./advanced.cordis.yml
|
||||
patches:
|
||||
- id: subprocess
|
||||
name: '@deepseek-ai/dsh-subprocess-local'
|
||||
disabled: true
|
||||
- id: fs-local
|
||||
name: '@deepseek-ai/dsh-fs-local'
|
||||
disabled: true
|
||||
- insert:
|
||||
- id: e2b
|
||||
name: '@deepseek-ai/dsh-e2b'
|
||||
config:
|
||||
cwd: !!js process.cwd()
|
||||
timeoutMs: 300000
|
||||
- id: subprocess-e2b
|
||||
name: '@deepseek-ai/dsh-subprocess-e2b'
|
||||
- id: fs-e2b
|
||||
name: '@deepseek-ai/dsh-fs-e2b'
|
||||
- id: sandbox-policy
|
||||
name: '@deepseek-ai/dsh-sandbox-policy'
|
||||
config:
|
||||
mode: danger-full-access
|
||||
workspaceRoot: !!js process.cwd()
|
||||
- id: pty
|
||||
name: '@deepseek-ai/dsh-pty'
|
||||
- id: pty-local
|
||||
name: '@deepseek-ai/dsh-pty-local'
|
||||
- id: tool-pty
|
||||
name: '@deepseek-ai/dsh-tool-pty'
|
||||
- id: lsp
|
||||
name: '@deepseek-ai/dsh-lsp'
|
||||
- id: lsp-local
|
||||
name: '@deepseek-ai/dsh-lsp-local'
|
||||
config:
|
||||
servers:
|
||||
typescript:
|
||||
command: npx
|
||||
args: [--yes, typescript-language-server@5.0.0, --stdio]
|
||||
extensionToLanguage:
|
||||
.ts: typescript
|
||||
.tsx: typescriptreact
|
||||
.js: javascript
|
||||
.jsx: javascriptreact
|
||||
- id: tool-lsp
|
||||
name: '@deepseek-ai/dsh-tool-lsp'
|
||||
@@ -0,0 +1,202 @@
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { resolve } from 'node:path'
|
||||
import { boot } from '@deepseek-ai/dsh-app-boot'
|
||||
import { Inbox } from '@deepseek-ai/dsh-agent'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { Session, SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type {} from '@deepseek-ai/dsh-fs-e2b'
|
||||
import type {} from '@deepseek-ai/dsh-bash-local'
|
||||
import type {} from '@deepseek-ai/dsh-lsp-local'
|
||||
import type {} from '@deepseek-ai/dsh-pty-local'
|
||||
|
||||
const configPath = process.argv[2]
|
||||
if (configPath === undefined) throw new Error('usage: bin.ts <cordis.yml>')
|
||||
|
||||
const ctx = await boot('e2b-composition', resolve(configPath))
|
||||
const ownerFiber = ctx.plugin(() => {})
|
||||
const ownerId = SessionId('e2b-live-owner')
|
||||
const session = Session.create(ownerId)
|
||||
const owner: Agent = {
|
||||
id: ownerId,
|
||||
options: {},
|
||||
session,
|
||||
inbox: new Inbox(session, { inserted: () => {}, discarded: () => {}, claimed: () => {} }),
|
||||
status: 'idle',
|
||||
ctx: ownerFiber.ctx,
|
||||
send() {},
|
||||
followup() {},
|
||||
steer() {},
|
||||
inject() {},
|
||||
cancel() {},
|
||||
runMaintenance: task => task(new AbortController().signal),
|
||||
whenIdle: () => Promise.resolve(),
|
||||
}
|
||||
const unregisterOwner = ctx.agents.register(owner)
|
||||
let terminalId: Awaited<ReturnType<typeof ctx.pty.spawn>>['sessionId'] | undefined
|
||||
try {
|
||||
const sandbox = await ctx.e2b.getSandbox()
|
||||
const fromFs = await ctx.fs.resolve('from-fs.txt')
|
||||
const written = await ctx.fs.writeText(fromFs, 'written-by-fs\n', { kind: 'createIfAbsent' })
|
||||
const observed = await ctx.fs.stat(fromFs)
|
||||
if (observed?.version !== written.version) {
|
||||
throw new Error(`E2B rename did not preserve version metadata: ${JSON.stringify({ written, observed })}`)
|
||||
}
|
||||
await ctx.fs.editText(
|
||||
fromFs,
|
||||
{ oldString: 'written-by-fs', newString: 'versioned-by-fs', replaceAll: false },
|
||||
{ version: observed.version },
|
||||
)
|
||||
const bashRead = await ctx.bash.run(ctx.bash.resolve({ command: 'cat from-fs.txt' }))
|
||||
if (bashRead.exitCode !== 0 || bashRead.stdout.text !== 'versioned-by-fs\n') {
|
||||
throw new Error(`E2B Bash could not read the FS write: ${JSON.stringify(bashRead)}`)
|
||||
}
|
||||
|
||||
const bashWrite = await ctx.bash.run(ctx.bash.resolve({ command: "printf 'written-by-bash\\n' > from-bash.txt" }))
|
||||
if (bashWrite.exitCode !== 0) {
|
||||
throw new Error(`E2B Bash could not write the shared filesystem: ${JSON.stringify(bashWrite)}`)
|
||||
}
|
||||
const fromBash = await ctx.fs.resolve('from-bash.txt')
|
||||
const fsRead = await ctx.fs.readText(fromBash)
|
||||
|
||||
const environmentHandle = ctx.subprocess.spawn({
|
||||
argv: ['env'],
|
||||
cwd: process.cwd(),
|
||||
stdio: { stdin: 'ignore', stdout: { maxBytes: 65_536 }, stderr: { maxBytes: 4_096 } },
|
||||
graceMs: 500,
|
||||
env: {
|
||||
'FOO-BAR': 'hyphen-value',
|
||||
DSH_EXPLICIT: 'managed-value',
|
||||
TOKEN_EXPLICIT: 'credential-value',
|
||||
},
|
||||
})
|
||||
const environmentOutcome = await environmentHandle.done
|
||||
const environmentText = environmentHandle.collected.stdout?.readFrom(0).text
|
||||
if (environmentOutcome.exitCode !== 0 || environmentText === undefined) {
|
||||
throw new Error(`E2B subprocess environment probe failed: ${JSON.stringify(environmentOutcome)}`)
|
||||
}
|
||||
const environmentLines = new Set(environmentText.trimEnd().split('\n'))
|
||||
const explicitEnvironment = [
|
||||
'FOO-BAR=hyphen-value',
|
||||
'DSH_EXPLICIT=managed-value',
|
||||
'TOKEN_EXPLICIT=credential-value',
|
||||
].every(entry => environmentLines.has(entry))
|
||||
if (!explicitEnvironment) throw new Error(`E2B subprocess dropped an explicit environment entry: ${environmentText}`)
|
||||
|
||||
const splitUtf8Handle = ctx.subprocess.spawn({
|
||||
argv: ['bash', '-c', "printf '\\344'; sleep 0.05; printf '\\275'; sleep 0.05; printf '\\240'; sleep 0.05; printf '\\345'; sleep 0.05; printf '\\245'; sleep 0.05; printf '\\275'"],
|
||||
cwd: process.cwd(),
|
||||
stdio: { stdin: 'ignore', stdout: { maxBytes: 32 }, stderr: { maxBytes: 4_096 } },
|
||||
graceMs: 500,
|
||||
env: {},
|
||||
})
|
||||
const splitUtf8Outcome = await splitUtf8Handle.done
|
||||
const splitUtf8Output = splitUtf8Handle.collected.stdout?.readFrom(0).text
|
||||
if (splitUtf8Outcome.exitCode !== 0 || splitUtf8Output !== '你好') {
|
||||
throw new Error(`E2B subprocess corrupted split UTF-8 output: ${JSON.stringify({ splitUtf8Outcome, splitUtf8Output })}`)
|
||||
}
|
||||
|
||||
const outputDrainStarted = Date.now()
|
||||
const outputDrainHandle = ctx.subprocess.spawn({
|
||||
argv: ['bash', '-c', "bash -c 'exec -a dsh-output-drain-descendant sleep 30' & printf 'leader-done\\n'"],
|
||||
cwd: process.cwd(),
|
||||
stdio: { stdin: 'ignore', stdout: { maxBytes: 64 }, stderr: { maxBytes: 4_096 } },
|
||||
graceMs: 250,
|
||||
env: {},
|
||||
})
|
||||
const outputDrainOutcome = await outputDrainHandle.done
|
||||
const outputDrainText = outputDrainHandle.collected.stdout?.readFrom(0).text
|
||||
const outputDrainElapsedMs = Date.now() - outputDrainStarted
|
||||
outputDrainHandle.terminate()
|
||||
const outputDrainExited = await outputDrainHandle.waitForExit(AbortSignal.timeout(5_000))
|
||||
const outputDrainProcesses = await sandbox.commands.list()
|
||||
const outputDrainClean = !outputDrainProcesses.some(processInfo =>
|
||||
JSON.stringify([processInfo.cmd, processInfo.args]).includes('dsh-output-drain-descendant'),
|
||||
)
|
||||
if (outputDrainOutcome.exitCode !== 0 || outputDrainText !== 'leader-done\n'
|
||||
|| outputDrainElapsedMs >= 10_000 || !outputDrainExited || !outputDrainClean) {
|
||||
throw new Error(`E2B subprocess did not bound descendant-held output: ${JSON.stringify({
|
||||
outputDrainOutcome, outputDrainText, outputDrainElapsedMs, outputDrainExited, outputDrainClean,
|
||||
})}`)
|
||||
}
|
||||
|
||||
const lspFixture = await readFile(new URL('./fixture-lsp.mjs', import.meta.url), 'utf8')
|
||||
const remoteLspFixture = await ctx.fs.resolve('fixture-lsp.mjs')
|
||||
await ctx.fs.writeText(remoteLspFixture, lspFixture, { kind: 'createIfAbsent' })
|
||||
const remoteSource = await ctx.fs.resolve('multibyte # file.ts')
|
||||
await ctx.fs.writeText(remoteSource, 'const café = "你好"\nconsole.log(café)\n', { kind: 'createIfAbsent' })
|
||||
const hover = await ctx.lsp.query({
|
||||
operation: 'hover',
|
||||
filePath: 'multibyte # file.ts',
|
||||
position: { line: 0, character: 7 },
|
||||
workspaceRoot: process.cwd(),
|
||||
})
|
||||
const definition = await ctx.lsp.query({
|
||||
operation: 'goToDefinition',
|
||||
filePath: 'multibyte # file.ts',
|
||||
position: { line: 0, character: 7 },
|
||||
workspaceRoot: process.cwd(),
|
||||
})
|
||||
|
||||
const terminal = await ctx.pty.spawn(owner, { type: 'shell' })
|
||||
terminalId = terminal.sessionId
|
||||
const terminalEcho = await ctx.pty.startSend(owner, terminal.sessionId, {
|
||||
text: "printf 'PTY-你好\\n'",
|
||||
submit: true,
|
||||
}).done
|
||||
const sleeping = ctx.pty.startSend(owner, terminal.sessionId, {
|
||||
text: "printf 'DSH_SLEEP_%s\\n' READY; sleep 30",
|
||||
submit: true,
|
||||
})
|
||||
let sleepReadyOutput = ''
|
||||
const sleepReadyDeadline = Date.now() + 5_000
|
||||
while (!sleepReadyOutput.includes('DSH_SLEEP_READY\n')) {
|
||||
sleepReadyOutput += sleeping.readOutput().delta
|
||||
if (sleepReadyOutput.includes('DSH_SLEEP_READY\n')) break
|
||||
const settled = await Promise.race([
|
||||
sleeping.done.then(result => ({ result })),
|
||||
new Promise<undefined>(resolveDelay => setTimeout(() => { resolveDelay(undefined) }, 25)),
|
||||
])
|
||||
if (settled !== undefined) {
|
||||
throw new Error(`E2B PTY successor settled before executing: ${JSON.stringify(settled.result)}`)
|
||||
}
|
||||
if (Date.now() >= sleepReadyDeadline) throw new Error(`E2B PTY successor did not execute: ${sleepReadyOutput}`)
|
||||
}
|
||||
const terminalSignal = await ctx.pty.signal(owner, terminal.sessionId, 'SIGINT')
|
||||
const interrupted = await sleeping.done
|
||||
const stubborn = await ctx.pty.startSend(owner, terminal.sessionId, {
|
||||
text: "bash -c 'trap \"\" TERM; exec sleep 30' & printf 'DSH_STUBBORN_PID=%s\\n' \"$!\"",
|
||||
submit: true,
|
||||
}).done
|
||||
const stubbornMatch = /DSH_STUBBORN_PID=([1-9][0-9]*)/.exec(stubborn.viewport)
|
||||
if (stubbornMatch?.[1] === undefined) throw new Error(`E2B PTY did not report its stubborn child: ${stubborn.viewport}`)
|
||||
const stubbornPid = Number(stubbornMatch[1])
|
||||
const terminalScrollback = ctx.pty.read(owner, terminal.sessionId, { count: 50 })
|
||||
await ctx.pty.kill(owner, terminal.sessionId, 'live E2B composition complete')
|
||||
terminalId = undefined
|
||||
const stubbornProbe = await sandbox.commands.run(`if kill -0 ${stubbornPid} 2>/dev/null; then printf alive; else printf gone; fi`)
|
||||
const terminalTreeCleanup = stubbornProbe.stdout === 'gone'
|
||||
if (!terminalTreeCleanup) throw new Error(`E2B PTY left process ${stubbornPid} alive after close`)
|
||||
|
||||
process.stdout.write(`${JSON.stringify({
|
||||
sandboxId: (await ctx.e2b.getSandbox()).sandboxId,
|
||||
bashRead: bashRead.stdout.text,
|
||||
fsRead,
|
||||
explicitEnvironment,
|
||||
splitUtf8Output,
|
||||
hover,
|
||||
definition,
|
||||
terminal: {
|
||||
motd: terminal.motd,
|
||||
echo: terminalEcho,
|
||||
signal: terminalSignal,
|
||||
interrupted,
|
||||
treeCleanup: terminalTreeCleanup,
|
||||
scrollback: terminalScrollback.text,
|
||||
},
|
||||
})}\n`)
|
||||
} finally {
|
||||
if (terminalId !== undefined) await ctx.pty.kill(owner, terminalId, 'fixture cleanup').catch(() => false)
|
||||
unregisterOwner()
|
||||
await ownerFiber.dispose()
|
||||
await ctx.fiber.dispose()
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
# One-world invariant (same pairing as examples/headless-agent/e2b.cordis.yml):
|
||||
# e2b.cwd and sandbox-policy.workspaceRoot must name the same remote directory,
|
||||
# which is also bash-local's implicit default workdir.
|
||||
- id: e2b
|
||||
name: '@deepseek-ai/dsh-e2b'
|
||||
config:
|
||||
cwd: !!js process.cwd()
|
||||
timeoutMs: 180000
|
||||
|
||||
- id: subprocess-e2b
|
||||
name: '@deepseek-ai/dsh-subprocess-e2b'
|
||||
|
||||
- id: bash
|
||||
name: '@deepseek-ai/dsh-bash-local'
|
||||
config:
|
||||
timeoutMs: 30000
|
||||
|
||||
- id: fs-e2b
|
||||
name: '@deepseek-ai/dsh-fs-e2b'
|
||||
|
||||
- id: agents
|
||||
name: '@deepseek-ai/dsh-agent'
|
||||
|
||||
- id: sandbox-policy
|
||||
name: '@deepseek-ai/dsh-sandbox-policy'
|
||||
config:
|
||||
mode: danger-full-access
|
||||
workspaceRoot: !!js process.cwd()
|
||||
|
||||
- id: pty
|
||||
name: '@deepseek-ai/dsh-pty'
|
||||
|
||||
- id: pty-local
|
||||
name: '@deepseek-ai/dsh-pty-local'
|
||||
config:
|
||||
pollIntervalMs: 25
|
||||
exactProbeAfterMs: 150
|
||||
idleSilenceMs: 2000
|
||||
handoffGraceMs: 500
|
||||
timeoutMs: 5000
|
||||
disposeGraceMs: 1000
|
||||
|
||||
- id: lsp
|
||||
name: '@deepseek-ai/dsh-lsp'
|
||||
|
||||
- id: lsp-local
|
||||
name: '@deepseek-ai/dsh-lsp-local'
|
||||
config:
|
||||
servers:
|
||||
fixture:
|
||||
command: node
|
||||
args:
|
||||
- !!js process.cwd() + '/fixture-lsp.mjs'
|
||||
extensionToLanguage:
|
||||
.ts: typescript
|
||||
shutdownTimeoutMs: 1000
|
||||
killGraceMs: 500
|
||||
@@ -0,0 +1,85 @@
|
||||
import { Buffer } from 'node:buffer'
|
||||
|
||||
let pending = Buffer.alloc(0)
|
||||
let source = ''
|
||||
let sourceUri = ''
|
||||
|
||||
function send(message) {
|
||||
const body = Buffer.from(JSON.stringify(message))
|
||||
process.stdout.write(`Content-Length: ${body.length}\r\n\r\n`)
|
||||
process.stdout.write(body)
|
||||
}
|
||||
|
||||
function respond(id, result) {
|
||||
send({ jsonrpc: '2.0', id, result })
|
||||
}
|
||||
|
||||
function dispatch(message) {
|
||||
switch (message.method) {
|
||||
case 'initialize':
|
||||
respond(message.id, {
|
||||
capabilities: {
|
||||
positionEncoding: 'utf-16',
|
||||
textDocumentSync: { openClose: true, change: 1 },
|
||||
definitionProvider: true,
|
||||
referencesProvider: true,
|
||||
implementationProvider: true,
|
||||
hoverProvider: true,
|
||||
},
|
||||
})
|
||||
return
|
||||
case 'textDocument/didOpen':
|
||||
source = message.params.textDocument.text
|
||||
sourceUri = message.params.textDocument.uri
|
||||
return
|
||||
case 'textDocument/didClose':
|
||||
source = ''
|
||||
sourceUri = ''
|
||||
return
|
||||
case 'textDocument/hover':
|
||||
if (!source.includes('const café = "你好"')) {
|
||||
send({ jsonrpc: '2.0', id: message.id, error: { code: -32000, message: 'multibyte source was corrupted' } })
|
||||
return
|
||||
}
|
||||
respond(message.id, {
|
||||
contents: { kind: 'markdown', value: '**remote hover** 你好 café' },
|
||||
range: { start: { line: 0, character: 6 }, end: { line: 0, character: 10 } },
|
||||
})
|
||||
return
|
||||
case 'textDocument/definition':
|
||||
case 'textDocument/references':
|
||||
case 'textDocument/implementation':
|
||||
respond(message.id, [{
|
||||
uri: sourceUri,
|
||||
range: { start: { line: 0, character: 6 }, end: { line: 0, character: 10 } },
|
||||
}])
|
||||
return
|
||||
case 'shutdown':
|
||||
respond(message.id, null)
|
||||
return
|
||||
case 'exit':
|
||||
process.exit(0)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
function drain() {
|
||||
for (;;) {
|
||||
const headerEnd = pending.indexOf('\r\n\r\n')
|
||||
if (headerEnd < 0) return
|
||||
const header = pending.subarray(0, headerEnd).toString('ascii')
|
||||
const match = /(?:^|\r\n)Content-Length: ([0-9]+)(?:\r\n|$)/i.exec(header)
|
||||
if (!match) throw new Error('missing Content-Length')
|
||||
const length = Number(match[1])
|
||||
const bodyStart = headerEnd + 4
|
||||
if (pending.length < bodyStart + length) return
|
||||
const body = pending.subarray(bodyStart, bodyStart + length)
|
||||
pending = pending.subarray(bodyStart + length)
|
||||
dispatch(JSON.parse(body.toString('utf8')))
|
||||
}
|
||||
}
|
||||
|
||||
process.stdin.on('data', chunk => {
|
||||
pending = Buffer.concat([pending, chunk])
|
||||
drain()
|
||||
})
|
||||
@@ -18,6 +18,9 @@
|
||||
mode: danger-full-access
|
||||
workspaceRoot: !!js process.env.DSH_CWD ?? process.cwd()
|
||||
|
||||
- id: subprocess
|
||||
name: '@deepseek-ai/dsh-subprocess-local'
|
||||
|
||||
- id: pty
|
||||
name: '@deepseek-ai/dsh-pty'
|
||||
|
||||
|
||||
@@ -26,7 +26,9 @@
|
||||
"@deepseek-ai/dsh-compact-basic": "workspace:*",
|
||||
"@deepseek-ai/dsh-compact-tool-result-prune": "workspace:*",
|
||||
"@deepseek-ai/dsh-credentials-local": "workspace:*",
|
||||
"@deepseek-ai/dsh-e2b": "workspace:*",
|
||||
"@deepseek-ai/dsh-fs-local": "workspace:*",
|
||||
"@deepseek-ai/dsh-fs-e2b": "workspace:*",
|
||||
"@deepseek-ai/dsh-fs-policy": "workspace:*",
|
||||
"@deepseek-ai/dsh-fs-sandbox": "workspace:^",
|
||||
"@deepseek-ai/dsh-goal": "workspace:*",
|
||||
@@ -76,6 +78,7 @@
|
||||
"@deepseek-ai/dsh-subagent-fork": "workspace:*",
|
||||
"@deepseek-ai/dsh-subagent-spawn": "workspace:*",
|
||||
"@deepseek-ai/dsh-subprocess-local": "workspace:*",
|
||||
"@deepseek-ai/dsh-subprocess-e2b": "workspace:*",
|
||||
"@deepseek-ai/dsh-system-prompt": "workspace:*",
|
||||
"@deepseek-ai/dsh-tasks-local": "workspace:*",
|
||||
"@deepseek-ai/dsh-time-context": "workspace:*",
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write examples/web-cordis/README.md
|
||||
README.md: 75c7610fecc20777607c7c8cbb6ed2fc7f590b73
|
||||
README.zh.md: 5c25347ee17037bbfc7d10a5507424ce52e3faff
|
||||
README.md: 21fe0a210b2e591a96dc254014a0f91ed9afa2ba
|
||||
README.zh.md: 35158affd5cbdd6f8fa7f5910500f21c2b241309
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
English | [中文](README.zh.md)
|
||||
|
||||
Self-referential demonstration of [`@deepseek-ai/dsh-tool-cordis`](../../packages/cordis/tool-cordis/README.md). The agent can inspect its current Cordis process and mount or unmount model-authored plugins in memory. Temporary plugins disappear when they are unmounted or the process exits and may affect other sessions in the same process.
|
||||
Self-referential demonstration of [`@deepseek-ai/dsh-tool-cordis`](../../packages/self-modification/tool-cordis/README.md). The agent can inspect its current Cordis process and mount or unmount model-authored plugins in memory. Temporary plugins disappear when they are unmounted or the process exits and may affect other sessions in the same process.
|
||||
|
||||
## Run it
|
||||
|
||||
@@ -18,4 +18,4 @@ Start the ACP automation server instead:
|
||||
pnpm run demo:cordis acp
|
||||
```
|
||||
|
||||
Both commands require `DEEPSEEK_API_KEY`. The [Cordis tool reference](../../packages/cordis/tool-cordis/README.md) owns the tool, lifecycle, and safety contracts.
|
||||
Both commands require `DEEPSEEK_API_KEY`. The [Cordis tool reference](../../packages/self-modification/tool-cordis/README.md) owns the tool, lifecycle, and safety contracts.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
[`@deepseek-ai/dsh-tool-cordis`](../../packages/cordis/tool-cordis/README.md) 的自指示例。agent(智能体)可以检查当前 Cordis 进程,并在内存中挂载或卸载模型编写的插件。临时插件会在卸载或进程退出时消失,并可能影响同一进程中的其他会话。
|
||||
[`@deepseek-ai/dsh-tool-cordis`](../../packages/self-modification/tool-cordis/README.md) 的自指示例。agent(智能体)可以检查当前 Cordis 进程,并在内存中挂载或卸载模型编写的插件。临时插件会在卸载或进程退出时消失,并可能影响同一进程中的其他会话。
|
||||
|
||||
## 运行
|
||||
|
||||
@@ -18,4 +18,4 @@ pnpm run demo:cordis
|
||||
pnpm run demo:cordis acp
|
||||
```
|
||||
|
||||
这两条命令都需要 `DEEPSEEK_API_KEY`。工具、生命周期和安全契约由 [Cordis 工具参考](../../packages/cordis/tool-cordis/README.md)定义。
|
||||
这两条命令都需要 `DEEPSEEK_API_KEY`。工具、生命周期和安全契约由 [Cordis 工具参考](../../packages/self-modification/tool-cordis/README.md)定义。
|
||||
|
||||
Reference in New Issue
Block a user