@namzu/sdk 53.0.0 → 53.2.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/CHANGELOG.md +20 -1
- package/dist/compaction/index.d.ts +1 -1
- package/dist/compaction/index.d.ts.map +1 -1
- package/dist/compaction/index.js +1 -1
- package/dist/compaction/index.js.map +1 -1
- package/dist/compaction/manual.d.ts +5 -0
- package/dist/compaction/manual.d.ts.map +1 -1
- package/dist/compaction/manual.js +4 -4
- package/dist/compaction/manual.js.map +1 -1
- package/dist/compaction/summary.d.ts +12 -2
- package/dist/compaction/summary.d.ts.map +1 -1
- package/dist/compaction/summary.js +13 -3
- package/dist/compaction/summary.js.map +1 -1
- package/dist/compaction/tool-result-editing.d.ts.map +1 -1
- package/dist/compaction/tool-result-editing.js +9 -6
- package/dist/compaction/tool-result-editing.js.map +1 -1
- package/dist/manager/agent/lifecycle.d.ts +15 -0
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +169 -1
- package/dist/manager/agent/lifecycle.js.map +1 -1
- package/dist/public-runtime.d.ts +2 -1
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +2 -1
- package/dist/public-runtime.js.map +1 -1
- package/dist/runtime/query/checkpoint.d.ts.map +1 -1
- package/dist/runtime/query/checkpoint.js +3 -1
- package/dist/runtime/query/checkpoint.js.map +1 -1
- package/dist/runtime/query/executor.d.ts +2 -0
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +21 -1
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +6 -2
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/runtime/query/tool-output-budget.d.ts +30 -2
- package/dist/runtime/query/tool-output-budget.d.ts.map +1 -1
- package/dist/runtime/query/tool-output-budget.js +43 -10
- package/dist/runtime/query/tool-output-budget.js.map +1 -1
- package/dist/scheduler/local.d.ts +2 -1
- package/dist/scheduler/local.d.ts.map +1 -1
- package/dist/scheduler/local.js +8 -0
- package/dist/scheduler/local.js.map +1 -1
- package/dist/store/evidence/disk.d.ts +3 -2
- package/dist/store/evidence/disk.d.ts.map +1 -1
- package/dist/store/evidence/disk.js +23 -3
- package/dist/store/evidence/disk.js.map +1 -1
- package/dist/store/evidence/source-text.d.ts +20 -0
- package/dist/store/evidence/source-text.d.ts.map +1 -1
- package/dist/store/evidence/source-text.js +29 -2
- package/dist/store/evidence/source-text.js.map +1 -1
- package/dist/store/session-log/core.d.ts.map +1 -1
- package/dist/store/session-log/core.js +28 -0
- package/dist/store/session-log/core.js.map +1 -1
- package/dist/store/session-log/spilled-input.d.ts +34 -0
- package/dist/store/session-log/spilled-input.d.ts.map +1 -0
- package/dist/store/session-log/spilled-input.js +68 -0
- package/dist/store/session-log/spilled-input.js.map +1 -0
- package/dist/tools/builtins/read-file.d.ts.map +1 -1
- package/dist/tools/builtins/read-file.js +7 -2
- package/dist/tools/builtins/read-file.js.map +1 -1
- package/dist/tools/retained-output.d.ts +12 -0
- package/dist/tools/retained-output.d.ts.map +1 -0
- package/dist/tools/retained-output.js +39 -0
- package/dist/tools/retained-output.js.map +1 -0
- package/dist/types/agent/manager.d.ts +48 -0
- package/dist/types/agent/manager.d.ts.map +1 -1
- package/dist/types/agent/scheduler.d.ts +20 -0
- package/dist/types/agent/scheduler.d.ts.map +1 -1
- package/dist/types/agent/task.d.ts +2 -0
- package/dist/types/agent/task.d.ts.map +1 -1
- package/dist/types/session/records.d.ts +25 -0
- package/dist/types/session/records.d.ts.map +1 -1
- package/dist/types/session/records.js +16 -0
- package/dist/types/session/records.js.map +1 -1
- package/dist/types/tool/index.d.ts +8 -0
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/tool/index.js.map +1 -1
- package/package.json +1 -1
- package/src/compaction/index.ts +6 -1
- package/src/compaction/manual.ts +15 -3
- package/src/compaction/summary.ts +17 -3
- package/src/compaction/tool-result-editing.ts +12 -9
- package/src/manager/agent/lifecycle.ts +181 -1
- package/src/public-runtime.ts +2 -1
- package/src/runtime/query/checkpoint.ts +6 -1
- package/src/runtime/query/executor.ts +25 -1
- package/src/runtime/query/iteration/phases/compaction.ts +13 -2
- package/src/runtime/query/tool-output-budget.ts +65 -11
- package/src/scheduler/local.ts +14 -1
- package/src/store/evidence/disk.ts +28 -3
- package/src/store/evidence/source-text.ts +38 -2
- package/src/store/session-log/core.ts +28 -0
- package/src/store/session-log/spilled-input.ts +90 -0
- package/src/tools/builtins/read-file.ts +7 -2
- package/src/tools/retained-output.ts +38 -0
- package/src/types/agent/manager.ts +54 -0
- package/src/types/agent/scheduler.ts +21 -0
- package/src/types/agent/task.ts +2 -0
- package/src/types/session/records.ts +16 -0
- package/src/types/tool/index.ts +8 -0
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
isLeaseLive,
|
|
52
52
|
} from './lease.js'
|
|
53
53
|
import type { SpillReadOptions, SpillRef, SpillStore } from './spill.js'
|
|
54
|
+
import { TOOL_INPUT_RECORD_TYPES, spilledInput, toolInputSlots } from './spilled-input.js'
|
|
54
55
|
|
|
55
56
|
// ─── public types ─────────────────────────────────────────────────────────
|
|
56
57
|
|
|
@@ -666,6 +667,9 @@ export class SessionLogCore implements SessionLog {
|
|
|
666
667
|
? await this.#spillStructuredResults(original)
|
|
667
668
|
: original
|
|
668
669
|
const type = candidate.type
|
|
670
|
+
if (typeof type === 'string' && TOOL_INPUT_RECORD_TYPES.has(type)) {
|
|
671
|
+
return this.#spillToolInputs(candidate)
|
|
672
|
+
}
|
|
669
673
|
if (
|
|
670
674
|
type !== 'message' &&
|
|
671
675
|
type !== 'message_replaced' &&
|
|
@@ -715,6 +719,30 @@ export class SessionLogCore implements SessionLog {
|
|
|
715
719
|
return { ...candidate, content: preview, spill: ref }
|
|
716
720
|
}
|
|
717
721
|
|
|
722
|
+
/**
|
|
723
|
+
* A record that carries tool inputs and would not fit a line: each large
|
|
724
|
+
* input goes to `tool-results/` and the record keeps a stand-in, so the
|
|
725
|
+
* person is still asked and the call is still restored on resume.
|
|
726
|
+
*/
|
|
727
|
+
async #spillToolInputs(candidate: Record<string, unknown>): Promise<Record<string, unknown>> {
|
|
728
|
+
const sizeOf = (record: Record<string, unknown>): number =>
|
|
729
|
+
Buffer.byteLength(`${JSON.stringify(record)}\n`, 'utf8')
|
|
730
|
+
if (sizeOf(candidate) <= this.#spillAbove) return candidate
|
|
731
|
+
const copy = structuredClone(candidate)
|
|
732
|
+
const slots = toolInputSlots(copy)
|
|
733
|
+
const bodies = slots.map(({ holder, key }) => JSON.stringify(holder[key]) ?? 'null')
|
|
734
|
+
// Largest first, and only as many as it takes to fit.
|
|
735
|
+
const order = slots.map((_, i) => i).sort((a, b) => bodies[b]!.length - bodies[a]!.length)
|
|
736
|
+
for (const index of order) {
|
|
737
|
+
const slot = slots[index]!
|
|
738
|
+
const body = bodies[index]!
|
|
739
|
+
const ref = await this.#spills.write(`input:${String(candidate.id)}:${index}`, 'text', body)
|
|
740
|
+
slot.holder[slot.key] = spilledInput(ref, body)
|
|
741
|
+
if (sizeOf(copy) <= this.#spillAbove) break
|
|
742
|
+
}
|
|
743
|
+
return copy
|
|
744
|
+
}
|
|
745
|
+
|
|
718
746
|
async #spillStructuredResults(
|
|
719
747
|
candidate: Record<string, unknown>,
|
|
720
748
|
): Promise<Record<string, unknown>> {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { SpillRef } from './spill.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A tool call's input can be larger than a session-log record may be (a
|
|
5
|
+
* write of a few megabytes, a batch of large writes). The record that carries
|
|
6
|
+
* it then holds this stand-in, and the input itself sits in `tool-results/`
|
|
7
|
+
* under the same rules as any other spilled body.
|
|
8
|
+
*/
|
|
9
|
+
export const SPILLED_INPUT_KEY = '$spilledInput'
|
|
10
|
+
|
|
11
|
+
/** Characters of the input kept inline beside the reference. */
|
|
12
|
+
const INPUT_PREVIEW_CHARS = 200
|
|
13
|
+
|
|
14
|
+
export interface SpilledInput {
|
|
15
|
+
readonly [SPILLED_INPUT_KEY]: SpillRef
|
|
16
|
+
readonly preview: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function spilledInput(ref: SpillRef, json: string): SpilledInput {
|
|
20
|
+
return { [SPILLED_INPUT_KEY]: ref, preview: json.slice(0, INPUT_PREVIEW_CHARS) }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function isSpilledInput(value: unknown): value is SpilledInput {
|
|
24
|
+
return (
|
|
25
|
+
typeof value === 'object' &&
|
|
26
|
+
value !== null &&
|
|
27
|
+
SPILLED_INPUT_KEY in value &&
|
|
28
|
+
typeof (value as Record<string, unknown>)[SPILLED_INPUT_KEY] === 'object'
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Where a record keeps tool inputs: the holder object and the key of the input in it. */
|
|
33
|
+
export interface InputSlot {
|
|
34
|
+
readonly holder: Record<string, unknown>
|
|
35
|
+
readonly key: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function callSlots(calls: unknown): InputSlot[] {
|
|
39
|
+
if (!Array.isArray(calls)) return []
|
|
40
|
+
const slots: InputSlot[] = []
|
|
41
|
+
for (const call of calls) {
|
|
42
|
+
if (typeof call === 'object' && call !== null && 'input' in call) {
|
|
43
|
+
slots.push({ holder: call as Record<string, unknown>, key: 'input' })
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return slots
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Record types that carry a tool call's whole input. */
|
|
50
|
+
export const TOOL_INPUT_RECORD_TYPES: ReadonlySet<string> = new Set([
|
|
51
|
+
'tool_review_requested',
|
|
52
|
+
'decision_requested',
|
|
53
|
+
'tool_executing',
|
|
54
|
+
'tool_input_completed',
|
|
55
|
+
])
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The input slots of one record. The record is read, never changed; the
|
|
59
|
+
* caller copies what it replaces.
|
|
60
|
+
*/
|
|
61
|
+
export function toolInputSlots(record: Record<string, unknown>): InputSlot[] {
|
|
62
|
+
switch (record.type) {
|
|
63
|
+
case 'tool_review_requested':
|
|
64
|
+
return callSlots(record.toolCalls)
|
|
65
|
+
case 'decision_requested': {
|
|
66
|
+
const request = record.request as { toolCalls?: unknown } | undefined
|
|
67
|
+
return request ? callSlots(request.toolCalls) : []
|
|
68
|
+
}
|
|
69
|
+
case 'tool_executing':
|
|
70
|
+
case 'tool_input_completed':
|
|
71
|
+
return 'input' in record ? [{ holder: record, key: 'input' }] : []
|
|
72
|
+
default:
|
|
73
|
+
return []
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The input slots of a request read back from a log, with any stand-in
|
|
79
|
+
* replaced by the input it stands for. Mutates and returns `request`.
|
|
80
|
+
*/
|
|
81
|
+
export async function restoreSpilledRequestInputs<T extends { toolCalls?: unknown }>(
|
|
82
|
+
request: T,
|
|
83
|
+
read: (ref: SpillRef) => Promise<string>,
|
|
84
|
+
): Promise<T> {
|
|
85
|
+
for (const { holder, key } of callSlots(request.toolCalls)) {
|
|
86
|
+
const value = holder[key]
|
|
87
|
+
if (isSpilledInput(value)) holder[key] = JSON.parse(await read(value[SPILLED_INPUT_KEY]))
|
|
88
|
+
}
|
|
89
|
+
return request
|
|
90
|
+
}
|
|
@@ -5,6 +5,7 @@ import type { ToolContext } from '../../types/tool/index.js'
|
|
|
5
5
|
import type { ToolFacts } from '../../types/tool/presentation.js'
|
|
6
6
|
import { defineTool } from '../defineTool.js'
|
|
7
7
|
import { resolveWithinAnyReal, toolRoots } from '../paths.js'
|
|
8
|
+
import { resolveRetainedOutput } from '../retained-output.js'
|
|
8
9
|
import { looksBinary } from '../text-window.js'
|
|
9
10
|
import { fingerprintContent } from './content-fingerprint.js'
|
|
10
11
|
import { type RenderedRead, renderCappedRead } from './read-render.js'
|
|
@@ -71,8 +72,12 @@ export const ReadFileTool = defineTool({
|
|
|
71
72
|
},
|
|
72
73
|
|
|
73
74
|
async execute(input, context) {
|
|
75
|
+
// This session's own retained tool output lives on the host, outside the
|
|
76
|
+
// roots and outside any sandbox. It is read here, as a plain window of text,
|
|
77
|
+
// because the result that named it told the model to.
|
|
78
|
+
const retained = await resolveRetainedOutput(context.retainedOutputDirectory, input.path)
|
|
74
79
|
// Sandbox-aware: route through sandbox.readFile() when available
|
|
75
|
-
if (context.sandbox) {
|
|
80
|
+
if (context.sandbox && retained === undefined) {
|
|
76
81
|
const buffer = await context.sandbox.readFile(input.path)
|
|
77
82
|
const binaryGuidance = describeStructuredBinaryRead(input.path, buffer)
|
|
78
83
|
if (binaryGuidance) {
|
|
@@ -112,7 +117,7 @@ export const ReadFileTool = defineTool({
|
|
|
112
117
|
}
|
|
113
118
|
}
|
|
114
119
|
|
|
115
|
-
const filePath = await resolveWithinAnyReal(toolRoots(context), input.path)
|
|
120
|
+
const filePath = retained ?? (await resolveWithinAnyReal(toolRoots(context), input.path))
|
|
116
121
|
// Refused on the size alone, before the file is in memory, when no window
|
|
117
122
|
// was named: the cap exists so a huge file is never loaded to be refused.
|
|
118
123
|
if (input.readRange === undefined && input.limit === undefined) {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { lstat, realpath } from 'node:fs/promises'
|
|
2
|
+
import { dirname, extname, isAbsolute, relative, resolve } from 'node:path'
|
|
3
|
+
|
|
4
|
+
function inside(root: string, path: string): boolean {
|
|
5
|
+
const rel = relative(root, path)
|
|
6
|
+
return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The real path of a retained tool-output file the model may read, or
|
|
11
|
+
* `undefined` when `candidate` is not one.
|
|
12
|
+
*
|
|
13
|
+
* Only this session's own retained directory counts. The file must be an
|
|
14
|
+
* absolute path to a regular `.txt` file that is not a symbolic link, has no
|
|
15
|
+
* other hard link, and whose real location is inside the directory's real
|
|
16
|
+
* location (so a link anywhere along the path, or `..`, takes it out). Nothing
|
|
17
|
+
* is created, and nothing but a read is offered.
|
|
18
|
+
*/
|
|
19
|
+
export async function resolveRetainedOutput(
|
|
20
|
+
directory: string | undefined,
|
|
21
|
+
candidate: string | undefined,
|
|
22
|
+
): Promise<string | undefined> {
|
|
23
|
+
if (!directory || !candidate || !isAbsolute(candidate) || candidate.includes('\0'))
|
|
24
|
+
return undefined
|
|
25
|
+
try {
|
|
26
|
+
const base = await realpath(directory)
|
|
27
|
+
const lexical = resolve(candidate)
|
|
28
|
+
if (extname(lexical).toLowerCase() !== '.txt') return undefined
|
|
29
|
+
if (!inside(resolve(directory), lexical) && !inside(base, lexical)) return undefined
|
|
30
|
+
const stat = await lstat(lexical)
|
|
31
|
+
if (stat.isSymbolicLink() || !stat.isFile() || stat.nlink !== 1) return undefined
|
|
32
|
+
const real = await realpath(lexical)
|
|
33
|
+
if (!inside(base, real) || dirname(real) === real) return undefined
|
|
34
|
+
return real
|
|
35
|
+
} catch {
|
|
36
|
+
return undefined
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -5,6 +5,48 @@ import type { SessionEventListener } from '../session/events.js'
|
|
|
5
5
|
import type { AgentLifecycleListener } from './lifecycle-event.js'
|
|
6
6
|
import type { AgentTask, AgentTaskContext, AgentTaskState, SendMessageOptions } from './task.js'
|
|
7
7
|
|
|
8
|
+
/** Why a saved child could not be made continuable again. */
|
|
9
|
+
export type RestoreChildRefusal =
|
|
10
|
+
/** No project layout is known, so there is no saved log to read. */
|
|
11
|
+
| 'no-layout'
|
|
12
|
+
/** Nothing readable is saved under that id for this parent. */
|
|
13
|
+
| 'not-saved'
|
|
14
|
+
/** The saved record belongs to another parent session. */
|
|
15
|
+
| 'foreign'
|
|
16
|
+
/** The saved record carries no continuation profile, was isolated, or names an agent the host cannot rebuild. */
|
|
17
|
+
| 'not-resumable'
|
|
18
|
+
/** The child never recorded an end: its process died mid-turn. */
|
|
19
|
+
| 'unsettled'
|
|
20
|
+
/** The child was cancelled. */
|
|
21
|
+
| 'stopped'
|
|
22
|
+
/** This manager already holds a continuation for it that is still running. */
|
|
23
|
+
| 'running'
|
|
24
|
+
/** Too many continuations are retained to take another. */
|
|
25
|
+
| 'full'
|
|
26
|
+
|
|
27
|
+
export interface RestoreChildOptions {
|
|
28
|
+
/** The saved child's session id. */
|
|
29
|
+
readonly childSessionId: SessionId
|
|
30
|
+
/** The directory the continued child works in; the same one it ran in. */
|
|
31
|
+
readonly workingDirectory: string
|
|
32
|
+
/**
|
|
33
|
+
* Which registered agent the child continues as, given the profile the host
|
|
34
|
+
* recorded at launch. Return `undefined` when the host cannot rebuild it.
|
|
35
|
+
*/
|
|
36
|
+
readonly agentFor: (profile: Readonly<Record<string, string>>) => string | undefined
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type RestoreChildResult =
|
|
40
|
+
| {
|
|
41
|
+
readonly status: 'restored'
|
|
42
|
+
readonly childSessionId: SessionId
|
|
43
|
+
/** The agent chosen by {@link RestoreChildOptions.agentFor}; pass it to `createTask` with `resumeSessionId`. */
|
|
44
|
+
readonly agentId: string
|
|
45
|
+
readonly profile: Readonly<Record<string, string>>
|
|
46
|
+
readonly description: string
|
|
47
|
+
}
|
|
48
|
+
| { readonly status: 'refused'; readonly reason: RestoreChildRefusal }
|
|
49
|
+
|
|
8
50
|
/**
|
|
9
51
|
* Agent task lifecycle contract — task creation, cancellation, messaging, and completion tracking.
|
|
10
52
|
* Concrete implementation: `AgentManager` in `manager/agent/lifecycle.ts`.
|
|
@@ -48,6 +90,18 @@ export interface AgentManagerContract {
|
|
|
48
90
|
on(listener: AgentLifecycleListener): void
|
|
49
91
|
off(listener: AgentLifecycleListener): void
|
|
50
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Rebuild the right to continue a child that finished in an earlier process
|
|
95
|
+
* from its saved log and meta document, under the invoking parent's scope.
|
|
96
|
+
* Optional: only a manager that can read saved children offers it. Grants
|
|
97
|
+
* nothing by itself; continuing still goes through `sendMessage` with
|
|
98
|
+
* `resumeSessionId`, with the invoking turn's authority and limits.
|
|
99
|
+
*/
|
|
100
|
+
restoreChildConversation?(
|
|
101
|
+
options: RestoreChildOptions,
|
|
102
|
+
context: AgentTaskContext,
|
|
103
|
+
): Promise<RestoreChildResult>
|
|
104
|
+
|
|
51
105
|
cleanup(): void
|
|
52
106
|
dispose(): void
|
|
53
107
|
}
|
|
@@ -5,6 +5,7 @@ import type { CancelCause } from '../session/cancel-cause.js'
|
|
|
5
5
|
import type { ChildSessionLifecycleEvent, SessionEventListener } from '../session/events.js'
|
|
6
6
|
import type { WorkspaceRef } from '../workspace/ref.js'
|
|
7
7
|
import type { AgentRuntimeContext, BaseAgentConfig, BaseAgentResult } from './base.js'
|
|
8
|
+
import type { RestoreChildOptions, RestoreChildResult } from './manager.js'
|
|
8
9
|
import type { AgentTaskState, ChildWorkspaceRequest } from './task.js'
|
|
9
10
|
|
|
10
11
|
export interface TaskHandle {
|
|
@@ -41,6 +42,15 @@ export interface CreateTaskOptions {
|
|
|
41
42
|
* and unchanged agent definition. A saved log alone grants no authority.
|
|
42
43
|
*/
|
|
43
44
|
readonly resumeSessionId?: SessionId
|
|
45
|
+
/**
|
|
46
|
+
* What a later process needs to rebuild this child, written to the child's
|
|
47
|
+
* saved meta document: at most 16 string entries, keys up to 64 and values up
|
|
48
|
+
* to 256 characters. The host defines the keys (which agent to rebuild,
|
|
49
|
+
* which model) and reads them back from `restoreChild`. Never put a secret
|
|
50
|
+
* here; it lands on disk beside the log. Absent, the child cannot be
|
|
51
|
+
* continued from disk after its process ends.
|
|
52
|
+
*/
|
|
53
|
+
readonly continuationProfile?: Readonly<Record<string, string>>
|
|
44
54
|
/** Per-task filesystem choice, forwarded to the local manager at admission. */
|
|
45
55
|
readonly workspace?: ChildWorkspaceRequest
|
|
46
56
|
/**
|
|
@@ -156,6 +166,17 @@ export interface TaskScheduler {
|
|
|
156
166
|
|
|
157
167
|
continueTask(taskId: TaskId, message: string): Promise<void>
|
|
158
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Make a child saved by an earlier process continuable again, from its saved
|
|
171
|
+
* log and meta document, under this scheduler's parent scope. OPTIONAL: only
|
|
172
|
+
* a scheduler that can read saved children offers it. It runs nothing; the
|
|
173
|
+
* caller then continues the child with `createTask({ resumeSessionId })`, so
|
|
174
|
+
* the invoking turn's authority and limits apply. See
|
|
175
|
+
* {@link CreateTaskOptions.continuationProfile} for what must have been
|
|
176
|
+
* recorded at launch.
|
|
177
|
+
*/
|
|
178
|
+
restoreChild?(options: RestoreChildOptions): Promise<RestoreChildResult>
|
|
179
|
+
|
|
159
180
|
/**
|
|
160
181
|
* Stop one task. A cause is relative to the child: a blocking delegation
|
|
161
182
|
* abandoned by the turn that launched it is cancelled by its `parent`.
|
package/src/types/agent/task.ts
CHANGED
|
@@ -238,6 +238,8 @@ export interface AgentTask {
|
|
|
238
238
|
export interface SendMessageOptions {
|
|
239
239
|
/** See {@link import('./scheduler.js').CreateTaskOptions.resumeSessionId}. */
|
|
240
240
|
readonly resumeSessionId?: SessionId
|
|
241
|
+
/** See {@link import('./scheduler.js').CreateTaskOptions.continuationProfile}. */
|
|
242
|
+
readonly continuationProfile?: Readonly<Record<string, string>>
|
|
241
243
|
/** Explicit child filesystem policy; omitted preserves legacy backend behavior. */
|
|
242
244
|
readonly workspace?: ChildWorkspaceRequest
|
|
243
245
|
/**
|
|
@@ -1039,6 +1039,22 @@ export const ChildSessionMetaSchema = z
|
|
|
1039
1039
|
status: turnExecutionStatus,
|
|
1040
1040
|
createdAt: isoUtc,
|
|
1041
1041
|
endedAt: isoUtc.optional(),
|
|
1042
|
+
/**
|
|
1043
|
+
* What a later process needs to continue this child from its saved log:
|
|
1044
|
+
* the filesystem policy it ran under and a small host-defined profile
|
|
1045
|
+
* (which agent to rebuild, which model). Absent on children saved before
|
|
1046
|
+
* this field existed and on children whose host recorded no profile; those
|
|
1047
|
+
* cannot be continued from disk.
|
|
1048
|
+
*/
|
|
1049
|
+
continuation: z
|
|
1050
|
+
.object({
|
|
1051
|
+
workspace: z.enum(['shared', 'isolated']),
|
|
1052
|
+
profile: z
|
|
1053
|
+
.record(z.string().min(1).max(64), z.string().max(256))
|
|
1054
|
+
.refine((value) => Object.keys(value).length <= 16, 'At most 16 profile entries'),
|
|
1055
|
+
})
|
|
1056
|
+
.strict()
|
|
1057
|
+
.optional(),
|
|
1042
1058
|
})
|
|
1043
1059
|
.strict()
|
|
1044
1060
|
export type ChildSessionMeta = z.infer<typeof ChildSessionMetaSchema>
|
package/src/types/tool/index.ts
CHANGED
|
@@ -458,6 +458,14 @@ export interface ToolContext {
|
|
|
458
458
|
* directory; an absolute path inside any of these is accepted.
|
|
459
459
|
*/
|
|
460
460
|
additionalDirectories?: readonly string[]
|
|
461
|
+
/**
|
|
462
|
+
* Absolute path of the directory holding THIS session's retained tool
|
|
463
|
+
* output (the files an oversized result was saved to). The `read` tool may
|
|
464
|
+
* open a regular file in it, and only that, without the path being a
|
|
465
|
+
* reviewed or added directory: the model was already shown the path in the
|
|
466
|
+
* result it came from. Set by the executor whenever the turn retains output.
|
|
467
|
+
*/
|
|
468
|
+
retainedOutputDirectory?: string
|
|
461
469
|
/**
|
|
462
470
|
* Absolute paths outside the working directory and the added directories
|
|
463
471
|
* that THIS call was approved to reach.
|