@namzu/sdk 55.0.0 → 56.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/dist/compaction/serializer.d.ts.map +1 -1
- package/dist/compaction/serializer.js +27 -15
- package/dist/compaction/serializer.js.map +1 -1
- package/dist/compaction/verifier.d.ts.map +1 -1
- package/dist/compaction/verifier.js +3 -2
- package/dist/compaction/verifier.js.map +1 -1
- package/dist/constants/compaction/index.d.ts +3 -1
- package/dist/constants/compaction/index.d.ts.map +1 -1
- package/dist/constants/compaction/index.js +3 -1
- package/dist/constants/compaction/index.js.map +1 -1
- package/dist/{manager/resident/learning-cycle.d.ts → learning/cycle.d.ts} +45 -36
- package/dist/learning/cycle.d.ts.map +1 -0
- package/dist/{manager/resident/learning-cycle.js → learning/cycle.js} +52 -20
- package/dist/learning/cycle.js.map +1 -0
- package/dist/learning/deprecated.d.ts +86 -0
- package/dist/learning/deprecated.d.ts.map +1 -0
- package/dist/learning/deprecated.js +22 -0
- package/dist/learning/deprecated.js.map +1 -0
- package/dist/learning/experience-store.d.ts +35 -0
- package/dist/learning/experience-store.d.ts.map +1 -0
- package/dist/learning/experience-store.js +143 -0
- package/dist/learning/experience-store.js.map +1 -0
- package/dist/learning/experience.d.ts +150 -0
- package/dist/learning/experience.d.ts.map +1 -0
- package/dist/learning/experience.js +146 -0
- package/dist/learning/experience.js.map +1 -0
- package/dist/learning/generator.d.ts +118 -0
- package/dist/learning/generator.d.ts.map +1 -0
- package/dist/learning/generator.js +275 -0
- package/dist/learning/generator.js.map +1 -0
- package/dist/learning/leak-tripwire.d.ts +66 -0
- package/dist/learning/leak-tripwire.d.ts.map +1 -0
- package/dist/learning/leak-tripwire.js +208 -0
- package/dist/learning/leak-tripwire.js.map +1 -0
- package/dist/{manager/resident/learning-observation.d.ts → learning/observation.d.ts} +4 -4
- package/dist/learning/observation.d.ts.map +1 -0
- package/dist/{manager/resident/learning-observation.js → learning/observation.js} +3 -3
- package/dist/learning/observation.js.map +1 -0
- package/dist/{manager/resident/learning.d.ts → learning/skill.d.ts} +29 -29
- package/dist/learning/skill.d.ts.map +1 -0
- package/dist/{manager/resident/learning.js → learning/skill.js} +27 -27
- package/dist/learning/skill.js.map +1 -0
- package/dist/{manager/resident/learning-store.d.ts → learning/store.d.ts} +38 -31
- package/dist/learning/store.d.ts.map +1 -0
- package/dist/{manager/resident/learning-store.js → learning/store.js} +52 -20
- package/dist/learning/store.js.map +1 -0
- package/dist/manager/resident/activity.d.ts +30 -6
- package/dist/manager/resident/activity.d.ts.map +1 -1
- package/dist/manager/resident/activity.js.map +1 -1
- package/dist/manager/resident/agenda.d.ts +42 -12
- package/dist/manager/resident/agenda.d.ts.map +1 -1
- package/dist/manager/resident/agenda.js +9 -7
- package/dist/manager/resident/agenda.js.map +1 -1
- package/dist/manager/resident/consumption.d.ts +27 -5
- package/dist/manager/resident/consumption.d.ts.map +1 -1
- package/dist/manager/resident/consumption.js +2 -0
- package/dist/manager/resident/consumption.js.map +1 -1
- package/dist/manager/resident/delivery-window.d.ts +7 -1
- package/dist/manager/resident/delivery-window.d.ts.map +1 -1
- package/dist/manager/resident/delivery-window.js +2 -0
- package/dist/manager/resident/delivery-window.js.map +1 -1
- package/dist/manager/resident/evidence-recall.d.ts +7 -1
- package/dist/manager/resident/evidence-recall.d.ts.map +1 -1
- package/dist/manager/resident/evidence-recall.js +2 -0
- package/dist/manager/resident/evidence-recall.js.map +1 -1
- package/dist/manager/resident/history.d.ts +45 -9
- package/dist/manager/resident/history.d.ts.map +1 -1
- package/dist/manager/resident/history.js.map +1 -1
- package/dist/manager/resident/host.d.ts +44 -10
- package/dist/manager/resident/host.d.ts.map +1 -1
- package/dist/manager/resident/host.js +4 -2
- package/dist/manager/resident/host.js.map +1 -1
- package/dist/manager/resident/initiative.d.ts +32 -6
- package/dist/manager/resident/initiative.d.ts.map +1 -1
- package/dist/manager/resident/initiative.js +2 -0
- package/dist/manager/resident/initiative.js.map +1 -1
- package/dist/manager/resident/loop.d.ts +22 -4
- package/dist/manager/resident/loop.d.ts.map +1 -1
- package/dist/manager/resident/loop.js +7 -1
- package/dist/manager/resident/loop.js.map +1 -1
- package/dist/manager/resident/outbox.d.ts +42 -8
- package/dist/manager/resident/outbox.d.ts.map +1 -1
- package/dist/manager/resident/outbox.js +2 -0
- package/dist/manager/resident/outbox.js.map +1 -1
- package/dist/manager/resident/proposal.d.ts +17 -3
- package/dist/manager/resident/proposal.d.ts.map +1 -1
- package/dist/manager/resident/proposal.js +2 -0
- package/dist/manager/resident/proposal.js.map +1 -1
- package/dist/manager/resident/store.d.ts +27 -5
- package/dist/manager/resident/store.d.ts.map +1 -1
- package/dist/manager/resident/store.js +7 -1
- package/dist/manager/resident/store.js.map +1 -1
- package/dist/manager/resident/tool-evidence.d.ts +45 -9
- package/dist/manager/resident/tool-evidence.d.ts.map +1 -1
- package/dist/manager/resident/tool-evidence.js +5 -1
- package/dist/manager/resident/tool-evidence.js.map +1 -1
- package/dist/prompt/resident-learning.d.ts +12 -2
- package/dist/prompt/resident-learning.d.ts.map +1 -1
- package/dist/prompt/resident-learning.js +6 -5
- package/dist/prompt/resident-learning.js.map +1 -1
- package/dist/prompt/resident-step.d.ts +10 -4
- package/dist/prompt/resident-step.d.ts.map +1 -1
- package/dist/prompt/resident-step.js +6 -4
- package/dist/prompt/resident-step.js.map +1 -1
- package/dist/public-runtime.d.ts +9 -3
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +10 -3
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-types.d.ts +9 -4
- package/dist/public-types.d.ts.map +1 -1
- package/dist/runtime/query/file-evidence-context.d.ts +4 -0
- package/dist/runtime/query/file-evidence-context.d.ts.map +1 -1
- package/dist/runtime/query/file-evidence-context.js +5 -1
- package/dist/runtime/query/file-evidence-context.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts +2 -0
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +19 -11
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/per-call-review.d.ts +9 -0
- package/dist/runtime/query/iteration/phases/per-call-review.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/per-call-review.js +19 -0
- package/dist/runtime/query/iteration/phases/per-call-review.js.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.js +4 -1
- package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
- package/dist/runtime/query/iteration/step-context.d.ts +42 -0
- package/dist/runtime/query/iteration/step-context.d.ts.map +1 -1
- package/dist/runtime/query/iteration/step-context.js +114 -0
- package/dist/runtime/query/iteration/step-context.js.map +1 -1
- package/dist/runtime/query/iteration/step-shaping.d.ts +9 -1
- package/dist/runtime/query/iteration/step-shaping.d.ts.map +1 -1
- package/dist/runtime/query/iteration/step-shaping.js +21 -11
- package/dist/runtime/query/iteration/step-shaping.js.map +1 -1
- package/dist/runtime/query/steering.d.ts +13 -0
- package/dist/runtime/query/steering.d.ts.map +1 -1
- package/dist/runtime/query/steering.js +42 -0
- package/dist/runtime/query/steering.js.map +1 -1
- package/dist/scheduler/completion-inbox.js +1 -1
- package/dist/scheduler/completion-inbox.js.map +1 -1
- package/dist/tools/builtins/bash.d.ts +3 -5
- package/dist/tools/builtins/bash.d.ts.map +1 -1
- package/dist/tools/builtins/bash.js +14 -15
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/builtins/edit.js +1 -1
- package/dist/tools/builtins/edit.js.map +1 -1
- package/dist/tools/env-scrub.d.ts +8 -0
- package/dist/tools/env-scrub.d.ts.map +1 -1
- package/dist/tools/env-scrub.js +13 -0
- package/dist/tools/env-scrub.js.map +1 -1
- package/dist/tools/resident-history.d.ts +2 -0
- package/dist/tools/resident-history.d.ts.map +1 -1
- package/dist/tools/resident-history.js +2 -0
- package/dist/tools/resident-history.js.map +1 -1
- package/dist/tools/resident-tool-evidence.d.ts +5 -1
- package/dist/tools/resident-tool-evidence.d.ts.map +1 -1
- package/dist/tools/resident-tool-evidence.js +5 -1
- package/dist/tools/resident-tool-evidence.js.map +1 -1
- package/dist/turn/evidence-recall.d.ts +15 -3
- package/dist/turn/evidence-recall.d.ts.map +1 -1
- package/dist/turn/evidence-recall.js +116 -27
- package/dist/turn/evidence-recall.js.map +1 -1
- package/dist/types/message/notice-frame.d.ts +1 -1
- package/dist/types/message/notice-frame.d.ts.map +1 -1
- package/dist/types/message/notice-frame.js +1 -0
- package/dist/types/message/notice-frame.js.map +1 -1
- package/package.json +1 -1
- package/src/compaction/serializer.ts +29 -17
- package/src/compaction/verifier.ts +3 -2
- package/src/constants/compaction/index.ts +3 -1
- package/src/{manager/resident/learning-cycle.ts → learning/cycle.ts} +130 -78
- package/src/learning/deprecated.ts +124 -0
- package/src/learning/experience-store.ts +202 -0
- package/src/learning/experience.ts +238 -0
- package/src/learning/generator.ts +388 -0
- package/src/learning/leak-tripwire.ts +280 -0
- package/src/{manager/resident/learning-observation.ts → learning/observation.ts} +5 -5
- package/src/{manager/resident/learning.ts → learning/skill.ts} +64 -67
- package/src/{manager/resident/learning-store.ts → learning/store.ts} +97 -67
- package/src/manager/resident/activity.ts +30 -6
- package/src/manager/resident/agenda.ts +63 -33
- package/src/manager/resident/consumption.ts +27 -5
- package/src/manager/resident/delivery-window.ts +7 -1
- package/src/manager/resident/evidence-recall.ts +7 -1
- package/src/manager/resident/history.ts +45 -9
- package/src/manager/resident/host.ts +45 -11
- package/src/manager/resident/initiative.ts +32 -6
- package/src/manager/resident/loop.ts +22 -4
- package/src/manager/resident/outbox.ts +42 -8
- package/src/manager/resident/proposal.ts +17 -3
- package/src/manager/resident/store.ts +27 -5
- package/src/manager/resident/tool-evidence.ts +45 -9
- package/src/prompt/resident-learning.ts +16 -7
- package/src/prompt/resident-step.ts +19 -13
- package/src/public-runtime.ts +35 -5
- package/src/public-types.ts +96 -34
- package/src/runtime/query/file-evidence-context.ts +8 -1
- package/src/runtime/query/iteration/index.ts +27 -13
- package/src/runtime/query/iteration/phases/per-call-review.ts +19 -0
- package/src/runtime/query/iteration/phases/tool-review.ts +3 -0
- package/src/runtime/query/iteration/step-context.ts +138 -0
- package/src/runtime/query/iteration/step-shaping.ts +39 -13
- package/src/runtime/query/steering.ts +51 -0
- package/src/scheduler/completion-inbox.ts +1 -1
- package/src/tools/builtins/bash.ts +14 -14
- package/src/tools/builtins/edit.ts +1 -1
- package/src/tools/env-scrub.ts +13 -0
- package/src/tools/resident-history.ts +2 -0
- package/src/tools/resident-tool-evidence.ts +5 -1
- package/src/turn/evidence-recall.ts +125 -30
- package/src/types/message/notice-frame.ts +3 -1
- package/dist/manager/resident/learning-cycle.d.ts.map +0 -1
- package/dist/manager/resident/learning-cycle.js.map +0 -1
- package/dist/manager/resident/learning-observation.d.ts.map +0 -1
- package/dist/manager/resident/learning-observation.js.map +0 -1
- package/dist/manager/resident/learning-store.d.ts.map +0 -1
- package/dist/manager/resident/learning-store.js.map +0 -1
- package/dist/manager/resident/learning.d.ts.map +0 -1
- package/dist/manager/resident/learning.js.map +0 -1
|
@@ -29,6 +29,25 @@ const NOT_THIS_QUESTION =
|
|
|
29
29
|
'That answer was not an answer to a question about a tool call, so this call was not run.'
|
|
30
30
|
|
|
31
31
|
/** The audit trail keeps a person's reason, cut short and without control characters. */
|
|
32
|
+
/**
|
|
33
|
+
* Whether a tool result is the answer to a call that never ran because the
|
|
34
|
+
* review did not let it: the person's No, a refusal that ended the turn, or a
|
|
35
|
+
* question that went unanswered. Such a result records a choice or an
|
|
36
|
+
* interruption, not a tool that failed, so a host counting failures leaves it
|
|
37
|
+
* out. Policy refusals and failed runs are not matched.
|
|
38
|
+
*/
|
|
39
|
+
export function isReviewRefusalResult(output: string): boolean {
|
|
40
|
+
if (!output.startsWith('Error: Tool "')) return false
|
|
41
|
+
return [
|
|
42
|
+
DECLINED_TOOL_CALL_FEEDBACK,
|
|
43
|
+
REFUSED_WITH_THE_TURN,
|
|
44
|
+
UNANSWERED_CALL,
|
|
45
|
+
EDIT_NOT_OFFERED,
|
|
46
|
+
PAUSED_CALL,
|
|
47
|
+
NOT_THIS_QUESTION,
|
|
48
|
+
].some((reason) => output.endsWith(` was not executed. ${reason}`) || output.endsWith(reason))
|
|
49
|
+
}
|
|
50
|
+
|
|
32
51
|
const AUDIT_NOTE_MAX_CHARS = 500
|
|
33
52
|
|
|
34
53
|
/**
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
formatMonitorNote,
|
|
29
29
|
takeNoticeMessage,
|
|
30
30
|
takeSteeringMessage,
|
|
31
|
+
takeWithheldEnvNotice,
|
|
31
32
|
} from '../../steering.js'
|
|
32
33
|
import { type IterationContext, awaitDecisionDurably } from './context.js'
|
|
33
34
|
import { PerCallReview } from './per-call-review.js'
|
|
@@ -295,6 +296,8 @@ export async function* runToolReview(
|
|
|
295
296
|
},
|
|
296
297
|
)
|
|
297
298
|
if (monitorNotice) ctx.recorder.pushMessage(monitorNotice)
|
|
299
|
+
const credentialNotice = takeWithheldEnvNotice(ctx.recorder.messages, batch.observations)
|
|
300
|
+
if (credentialNotice) ctx.recorder.pushMessage(credentialNotice)
|
|
298
301
|
if (jobNoticeDelivered) ctx.onJobNoticeDelivered?.()
|
|
299
302
|
if (monitorNoticeDelivered) ctx.onMonitorNoticeDelivered?.()
|
|
300
303
|
// The complete tool-result batch is already in history before host policy
|
|
@@ -44,3 +44,141 @@ export function placeBeforeLatestOperatorMessage(history: Message[], blocks: rea
|
|
|
44
44
|
}
|
|
45
45
|
history.splice(at, 0, ...blocks)
|
|
46
46
|
}
|
|
47
|
+
|
|
48
|
+
interface PlacedBlock {
|
|
49
|
+
readonly text: string
|
|
50
|
+
/** The operator message this block precedes, or the history message it follows. */
|
|
51
|
+
at: { readonly before: Message } | { readonly after: Message | undefined }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function textOf(message: Message): string {
|
|
55
|
+
return typeof message.content === 'string' ? message.content : JSON.stringify(message.content)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The operator message the model is about to answer, when there is one. */
|
|
59
|
+
function pendingOperatorMessage(history: readonly Message[]): Message | undefined {
|
|
60
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
61
|
+
const message = history[i] as Message
|
|
62
|
+
if (isOperatorUserMessage(message)) return message
|
|
63
|
+
if (message.role === 'assistant' || message.role === 'tool') return undefined
|
|
64
|
+
}
|
|
65
|
+
return undefined
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** A block whose own text says that a later one replaces it, under the name of what it reports. */
|
|
69
|
+
export interface ReplaceableBlock {
|
|
70
|
+
readonly key: string
|
|
71
|
+
readonly message: Message
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** How many superseded versions of a replaceable block still go with the latest. */
|
|
75
|
+
export const EARLIER_VERSIONS_KEPT = 2
|
|
76
|
+
|
|
77
|
+
interface VersionedBlock extends PlacedBlock {
|
|
78
|
+
readonly message: Message
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Where a turn's request-only data sits, so it stays put from call to call.
|
|
83
|
+
*
|
|
84
|
+
* What a block says is the step's own business: a block it offers again with
|
|
85
|
+
* the same text is the same block, and it keeps the place it first had; a
|
|
86
|
+
* block it no longer offers is withdrawn, so corrected data never lingers.
|
|
87
|
+
* A block seen for the first time goes before the operator's message when that
|
|
88
|
+
* message is what the model is answering, and otherwise after the history of
|
|
89
|
+
* that moment. So the requests of a turn share their opening up to the first
|
|
90
|
+
* block that changed, and a caching driver keeps reading the same prefix
|
|
91
|
+
* instead of re-reading the turn from the operator's message on.
|
|
92
|
+
*
|
|
93
|
+
* A block that says of itself that a later one replaces it (the file
|
|
94
|
+
* evidence) is never taken back. When its text changes, the earlier text stays
|
|
95
|
+
* where it went and the new text follows the history of that moment, which is
|
|
96
|
+
* how the block itself tells the model to read it; when it stops holding, a
|
|
97
|
+
* short block says so. Only the latest version and the last
|
|
98
|
+
* `EARLIER_VERSIONS_KEPT` before it are carried, and an earlier version whose
|
|
99
|
+
* place a compaction rewrote is dropped, so the stale text stays small. Until
|
|
100
|
+
* a version is dropped no request of the turn rewrites the one before it.
|
|
101
|
+
*
|
|
102
|
+
* One carry per turn. A position whose message has left the history (a
|
|
103
|
+
* compaction rewrote it) is given up and the block follows the history.
|
|
104
|
+
*/
|
|
105
|
+
export class TurnContextCarry {
|
|
106
|
+
private placed = new Map<string, PlacedBlock>()
|
|
107
|
+
private readonly versions = new Map<string, VersionedBlock[]>()
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Put `blocks` and the `replaceable` ones into `request`, the projected copy
|
|
111
|
+
* of `history` (same length and order). `end` is the last message of the
|
|
112
|
+
* turn's own history. `retractions` says, per replaceable key, what to
|
|
113
|
+
* write when an earlier block of that key no longer holds.
|
|
114
|
+
*/
|
|
115
|
+
place(
|
|
116
|
+
request: Message[],
|
|
117
|
+
history: readonly Message[],
|
|
118
|
+
end: Message | undefined,
|
|
119
|
+
blocks: readonly Message[],
|
|
120
|
+
replaceable: readonly ReplaceableBlock[] = [],
|
|
121
|
+
retractions: Readonly<Record<string, Message>> = {},
|
|
122
|
+
): void {
|
|
123
|
+
const index = (message: Message | undefined) =>
|
|
124
|
+
message ? history.lastIndexOf(message) : history.length - 1
|
|
125
|
+
const operator = pendingOperatorMessage(history)
|
|
126
|
+
const positionOf = (slot: PlacedBlock) =>
|
|
127
|
+
'before' in slot.at ? index(slot.at.before) : index(slot.at.after) + 1
|
|
128
|
+
const anchorOf = (slot: PlacedBlock) => ('before' in slot.at ? slot.at.before : slot.at.after)
|
|
129
|
+
const next = new Map<string, PlacedBlock>()
|
|
130
|
+
const inserts: { at: number; message: Message }[] = []
|
|
131
|
+
for (const message of blocks) {
|
|
132
|
+
const text = textOf(message)
|
|
133
|
+
if (next.has(text)) continue
|
|
134
|
+
let slot: PlacedBlock | undefined = this.placed.get(text)
|
|
135
|
+
if (slot) {
|
|
136
|
+
const anchor = anchorOf(slot)
|
|
137
|
+
if (anchor && index(anchor) < 0) slot = { text, at: { after: end } }
|
|
138
|
+
} else {
|
|
139
|
+
slot = { text, at: operator ? { before: operator } : { after: end } }
|
|
140
|
+
}
|
|
141
|
+
next.set(text, slot)
|
|
142
|
+
inserts.push({ at: positionOf(slot), message })
|
|
143
|
+
}
|
|
144
|
+
this.placed = next
|
|
145
|
+
|
|
146
|
+
const offered = new Map(replaceable.map((block) => [block.key, block.message]))
|
|
147
|
+
for (const key of new Set([...this.versions.keys(), ...offered.keys()])) {
|
|
148
|
+
const held = this.versions.get(key) ?? []
|
|
149
|
+
const wanted = offered.get(key) ?? retractions[key]
|
|
150
|
+
const last = held.at(-1)
|
|
151
|
+
if (wanted !== undefined && (last === undefined || last.text !== textOf(wanted)))
|
|
152
|
+
held.push({
|
|
153
|
+
text: textOf(wanted),
|
|
154
|
+
message: wanted,
|
|
155
|
+
// The first version goes where the other blocks go; a later one
|
|
156
|
+
// follows the history, after the one it replaces.
|
|
157
|
+
at: last === undefined && operator ? { before: operator } : { after: end },
|
|
158
|
+
})
|
|
159
|
+
// An earlier version whose place a compaction rewrote is given up: the
|
|
160
|
+
// request is rewritten at that point anyway, and stacking stale copies
|
|
161
|
+
// at the end would only add bulk. The latest version is never given up.
|
|
162
|
+
const kept = held.filter((version, i) => {
|
|
163
|
+
if (i === held.length - 1) return true
|
|
164
|
+
const anchor = anchorOf(version)
|
|
165
|
+
return !anchor || index(anchor) >= 0
|
|
166
|
+
})
|
|
167
|
+
// Only the latest and a few earlier versions are carried, so a long turn
|
|
168
|
+
// does not grow by a full copy per change.
|
|
169
|
+
kept.splice(0, Math.max(0, kept.length - (EARLIER_VERSIONS_KEPT + 1)))
|
|
170
|
+
this.versions.set(key, kept)
|
|
171
|
+
for (const version of kept) {
|
|
172
|
+
const anchor = anchorOf(version)
|
|
173
|
+
if (anchor && index(anchor) < 0) version.at = { after: end }
|
|
174
|
+
inserts.push({ at: positionOf(version), message: version.message })
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Back to front, so an earlier position is not shifted by a later one;
|
|
179
|
+
// blocks at one position keep the order they were offered in.
|
|
180
|
+
const order = inserts.map((entry, position) => ({ ...entry, position }))
|
|
181
|
+
order.sort((a, b) => b.at - a.at || b.position - a.position)
|
|
182
|
+
for (const entry of order) request.splice(entry.at, 0, entry.message)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -20,7 +20,11 @@ import { toErrorMessage } from '../../../utils/error.js'
|
|
|
20
20
|
import { createCallbackInference } from '../callback-inference.js'
|
|
21
21
|
import { measureContext } from './phases/compaction.js'
|
|
22
22
|
import type { IterationContext } from './phases/index.js'
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
type ReplaceableBlock,
|
|
25
|
+
placeBeforeLatestOperatorMessage,
|
|
26
|
+
stepContextMessage,
|
|
27
|
+
} from './step-context.js'
|
|
24
28
|
|
|
25
29
|
/**
|
|
26
30
|
* How the next request is shaped: admission, preparation, and the context
|
|
@@ -51,28 +55,50 @@ export function appendWorkContext(
|
|
|
51
55
|
stepNumber: number,
|
|
52
56
|
prepared: PrepareStepResult,
|
|
53
57
|
): void {
|
|
58
|
+
// Per-turn data goes in front of the operator's new message while that is
|
|
59
|
+
// what the model is answering, like the other blocks of request-only data.
|
|
60
|
+
placeBeforeLatestOperatorMessage(
|
|
61
|
+
messages,
|
|
62
|
+
workContextBlocks(shaping, messages, stepNumber, prepared).map((block) => block.message),
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** What the two work-context blocks report; each tells the model a later one replaces it. */
|
|
67
|
+
export const OWNED_WORK_KEY = 'owned-work'
|
|
68
|
+
export const FILE_EVIDENCE_KEY = 'file-evidence'
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The work-context blocks for a request whose projected messages are
|
|
72
|
+
* `messages`, each under the name of what it reports.
|
|
73
|
+
*/
|
|
74
|
+
export function workContextBlocks(
|
|
75
|
+
shaping: StepShaping,
|
|
76
|
+
messages: readonly Message[],
|
|
77
|
+
stepNumber: number,
|
|
78
|
+
prepared: PrepareStepResult,
|
|
79
|
+
): ReplaceableBlock[] {
|
|
54
80
|
const { ctx } = shaping
|
|
55
81
|
|
|
56
|
-
const contributions =
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
82
|
+
const contributions = (
|
|
83
|
+
[
|
|
84
|
+
[OWNED_WORK_KEY, ctx.completionInbox?.describeOwnedWork()],
|
|
85
|
+
[FILE_EVIDENCE_KEY, ctx.toolExecutor.describeFileEvidence(messages)],
|
|
86
|
+
] as [string, string | undefined][]
|
|
87
|
+
).filter((entry): entry is [string, string] => Boolean(entry[1]))
|
|
88
|
+
if (contributions.length === 0) return []
|
|
89
|
+
const placed: ReplaceableBlock[] = []
|
|
62
90
|
let room = stepContext(shaping, stepNumber, prepared).contextBudget?.remainingTokens ?? 0
|
|
63
91
|
// Leave room for the actual task; admit whole contributions, never dangling partial references.
|
|
64
|
-
if (room < 1_500) return
|
|
65
|
-
for (const content of contributions) {
|
|
92
|
+
if (room < 1_500) return []
|
|
93
|
+
for (const [key, content] of contributions) {
|
|
66
94
|
if (!content || content.length > 8_000) continue
|
|
67
95
|
const message = stepContextMessage(content)
|
|
68
96
|
const tokens = estimateMessageTokens(message)
|
|
69
97
|
if (tokens > Math.min(2_000, room - 1_000)) continue
|
|
70
|
-
placed.push(message)
|
|
98
|
+
placed.push({ key, message })
|
|
71
99
|
room -= tokens
|
|
72
100
|
}
|
|
73
|
-
|
|
74
|
-
// what the model is answering, like the other blocks of request-only data.
|
|
75
|
-
placeBeforeLatestOperatorMessage(messages, placed)
|
|
101
|
+
return placed
|
|
76
102
|
}
|
|
77
103
|
|
|
78
104
|
export function stepContext(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { withheldEnvNoticeText } from '../../tools/env-scrub.js'
|
|
1
2
|
import { createRuntimeContextMessage } from '../../types/message/index.js'
|
|
2
3
|
import type { Message, UserMessage } from '../../types/message/index.js'
|
|
3
4
|
import { NOTICE_TAG, frameNotice, readOperatorNotice } from '../../types/message/notice-frame.js'
|
|
@@ -377,3 +378,53 @@ export function attachSteering(
|
|
|
377
378
|
onDelivered?.(guidance)
|
|
378
379
|
return next
|
|
379
380
|
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* The count of credential-shaped variables a settled batch's shell results
|
|
384
|
+
* report as removed, or 0. The count rides in a result's `data`, never in the
|
|
385
|
+
* text the model reads.
|
|
386
|
+
*/
|
|
387
|
+
function withheldEnvCount(
|
|
388
|
+
observations: readonly { readonly result: { readonly data?: unknown } }[],
|
|
389
|
+
) {
|
|
390
|
+
let count = 0
|
|
391
|
+
for (const { result } of observations) {
|
|
392
|
+
const data = result.data
|
|
393
|
+
if (typeof data !== 'object' || data === null) continue
|
|
394
|
+
const n = (data as { withheldEnv?: unknown }).withheldEnv
|
|
395
|
+
if (typeof n === 'number' && Number.isInteger(n) && n > count) count = n
|
|
396
|
+
}
|
|
397
|
+
return count
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* The credential notice for a settled tool batch, as a user-role message of its
|
|
402
|
+
* own, or `undefined` when the batch removed nothing or the conversation
|
|
403
|
+
* already carries this very notice. It is sent once, and again only when the
|
|
404
|
+
* number of removed variables changes, so a run of failing commands does not
|
|
405
|
+
* repeat it. The conversation is the record, so a resumed or compacted one
|
|
406
|
+
* needs no counter of its own.
|
|
407
|
+
*/
|
|
408
|
+
export function takeWithheldEnvNotice(
|
|
409
|
+
history: readonly Message[],
|
|
410
|
+
observations: readonly { readonly result: { readonly data?: unknown } }[],
|
|
411
|
+
): UserMessage | undefined {
|
|
412
|
+
const count = withheldEnvCount(observations)
|
|
413
|
+
if (count === 0) return undefined
|
|
414
|
+
const body = withheldEnvNoticeText(count)
|
|
415
|
+
const said = history.some(
|
|
416
|
+
(message) =>
|
|
417
|
+
message.role === 'user' &&
|
|
418
|
+
message.source?.type === 'runtime-context' &&
|
|
419
|
+
message.source.kind === 'reminder' &&
|
|
420
|
+
typeof message.content === 'string' &&
|
|
421
|
+
message.content.startsWith(`<${NOTICE_TAG}-`) &&
|
|
422
|
+
message.content.includes(' kind="credentials">') &&
|
|
423
|
+
message.content.includes(body),
|
|
424
|
+
)
|
|
425
|
+
if (said) return undefined
|
|
426
|
+
return createRuntimeContextMessage(
|
|
427
|
+
frameNotice('credentials', '[Command environment]', body),
|
|
428
|
+
'reminder',
|
|
429
|
+
)
|
|
430
|
+
}
|
|
@@ -479,7 +479,7 @@ export class CompletionInbox {
|
|
|
479
479
|
stillRunningNotShown > 0
|
|
480
480
|
? ` ${shownRunning.length} running tasks are shown below, and ${stillRunningNotShown} more still running.`
|
|
481
481
|
: ''
|
|
482
|
-
return `Owned delegated work (current scheduler observations).${overflow} Result delivery is not proof that you answered the original request. Answer the latest operator message and complete the requested synthesis of available results unless the operator cancelled or changed that request. Reuse delivered results when sufficient; do not relaunch completed work. If a delivered result is no longer visible, retrieve it by task ID. A terminal state with a non-end_turn stop reason is not successful task completion. Do not repeat a synthesis already given.\n${JSON.stringify({ tasks, omitted: this.ours.size - ids.length })}`
|
|
482
|
+
return `Owned delegated work (current scheduler observations; a later block with this heading replaces this one).${overflow} Result delivery is not proof that you answered the original request. Answer the latest operator message and complete the requested synthesis of available results unless the operator cancelled or changed that request. Reuse delivered results when sufficient; do not relaunch completed work. If a delivered result is no longer visible, retrieve it by task ID. A terminal state with a non-end_turn stop reason is not successful task completion. Do not repeat a synthesis already given.\n${JSON.stringify({ tasks, omitted: this.ours.size - ids.length })}`
|
|
483
483
|
}
|
|
484
484
|
|
|
485
485
|
/**
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
withoutBashStartup,
|
|
21
21
|
} from '../command-shell.js'
|
|
22
22
|
import { defineTool } from '../defineTool.js'
|
|
23
|
-
import { scrubInheritedEnv } from '../env-scrub.js'
|
|
23
|
+
import { scrubInheritedEnv, withheldEnvNoticeText } from '../env-scrub.js'
|
|
24
24
|
import { createHostOutputDecoder } from '../host-output.js'
|
|
25
25
|
import { looksBinary, windowText } from '../text-window.js'
|
|
26
26
|
import { judgeExit } from './exit-semantics.js'
|
|
@@ -802,10 +802,10 @@ const BashToolBase = defineTool({
|
|
|
802
802
|
failure.stdoutTruncated ? 'stdout' : '',
|
|
803
803
|
failure.stderrTruncated ? 'stderr' : '',
|
|
804
804
|
].filter(Boolean)
|
|
805
|
-
// Only
|
|
806
|
-
//
|
|
807
|
-
//
|
|
808
|
-
//
|
|
805
|
+
// Only the failure path reports removed variables (in `data`, for the
|
|
806
|
+
// kernel's notice). A successful command did not need to know; a failing
|
|
807
|
+
// one is where "authentication failed" has to be distinguishable from
|
|
808
|
+
// "the variable was withheld".
|
|
809
809
|
// A status the command uses to answer (no match, files differ) is a
|
|
810
810
|
// result, not a failure; the model sees it as an ordinary call.
|
|
811
811
|
const judged =
|
|
@@ -816,7 +816,6 @@ const BashToolBase = defineTool({
|
|
|
816
816
|
clipped.length > 0
|
|
817
817
|
? `[${clipped.join(' and ')} ${clipped.length === 1 ? 'was' : 'were'} truncated by the host output cap. The omitted output is unavailable in this result.]`
|
|
818
818
|
: '',
|
|
819
|
-
answered ? '' : describeWithheldEnv(inherited.dropped),
|
|
820
819
|
]
|
|
821
820
|
.filter(Boolean)
|
|
822
821
|
.join('\n\n')
|
|
@@ -827,6 +826,11 @@ const BashToolBase = defineTool({
|
|
|
827
826
|
data: {
|
|
828
827
|
...(exitCode !== undefined ? { exitCode } : {}),
|
|
829
828
|
...(judged?.note ? { exitNote: judged.note } : {}),
|
|
829
|
+
// How many variables were removed, for the kernel's own notice; the
|
|
830
|
+
// model's result stays what the command printed.
|
|
831
|
+
...(!answered && inherited.dropped.length > 0
|
|
832
|
+
? { withheldEnv: inherited.dropped.length }
|
|
833
|
+
: {}),
|
|
830
834
|
...(leaveSandbox ? { sandboxed: false, sandboxEscape: true } : {}),
|
|
831
835
|
timedOut,
|
|
832
836
|
stdoutTruncated: failure.stdoutTruncated ?? false,
|
|
@@ -904,16 +908,12 @@ function formatShellOutput(stdout: string | undefined, stderr: string | undefine
|
|
|
904
908
|
}
|
|
905
909
|
|
|
906
910
|
/**
|
|
907
|
-
*
|
|
908
|
-
* environment
|
|
909
|
-
*
|
|
910
|
-
* A command that failed because it wanted one otherwise reports an
|
|
911
|
-
* authentication error pointing nowhere.
|
|
911
|
+
* The notice text for the credential-shaped variables left out of a command's
|
|
912
|
+
* environment (see {@link withheldEnvNoticeText}). It is sent as a notice after
|
|
913
|
+
* the tool batch, never written into the command's result.
|
|
912
914
|
*/
|
|
913
915
|
export function describeWithheldEnv(dropped: readonly string[]): string {
|
|
914
|
-
|
|
915
|
-
const one = dropped.length === 1
|
|
916
|
-
return `NOTE: ${dropped.length} credential-shaped environment ${one ? 'variable was' : 'variables were'} removed from this command's environment, so ${one ? 'it reads' : 'they read'} as unset. Ask the user if the command needs ${one ? 'it' : 'one'}.`
|
|
916
|
+
return withheldEnvNoticeText(dropped.length)
|
|
917
917
|
}
|
|
918
918
|
|
|
919
919
|
/**
|
|
@@ -428,7 +428,7 @@ function refusedEdit(result: EditApplyFailure, path: string) {
|
|
|
428
428
|
export const EditTool = defineTool({
|
|
429
429
|
name: 'edit',
|
|
430
430
|
description:
|
|
431
|
-
'Makes targeted edits to a file using exact string find-and-replace or line insertion. To change an existing file, use `edit`: `write` overwrites the whole body and discards earlier work on partial failure, while `edit` keeps the rest of the file byte-for-byte intact and is recoverable: if a single edit fails (old_string/oldStr ambiguous, broader restructuring needed), follow up with another `edit` instead of re-emitting the entire file via `write`. The old_string/oldStr must be unique in the file unless replace_all is true. For insertions, pass insertLine plus new_string/newStr; use insertLine: "end" to extend a file at the end.
|
|
431
|
+
'Makes targeted edits to a file using exact string find-and-replace or line insertion. To change an existing file, use `edit`: `write` overwrites the whole body and discards earlier work on partial failure, while `edit` keeps the rest of the file byte-for-byte intact and is recoverable: if a single edit fails (old_string/oldStr ambiguous, broader restructuring needed), follow up with another `edit` instead of re-emitting the entire file via `write`. The old_string/oldStr must be unique in the file unless replace_all is true. For insertions, pass insertLine plus new_string/newStr; use insertLine: "end" to extend a file at the end. Keep new_string/newStr under 12000 characters, because a longer payload can be cut off mid-call and fail; use repeated bounded edits for long sections. Preserves file formatting and indentation. To make several changes to one file that only make sense together, send them as `edits`: a list of {old_string, new_string} applied in order and committed as one write — if any of them does not apply, nothing is written and the reply names which one, so the file is never left half-changed.',
|
|
432
432
|
inputSchema,
|
|
433
433
|
modelInputSchema,
|
|
434
434
|
enforceModelInput: true,
|
package/src/tools/env-scrub.ts
CHANGED
|
@@ -91,3 +91,16 @@ export function scrubInheritedEnv(source: NodeJS.ProcessEnv = process.env): Scru
|
|
|
91
91
|
dropped.sort()
|
|
92
92
|
return { env, dropped }
|
|
93
93
|
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* What the model is told, once, when shell commands run with credential-shaped
|
|
97
|
+
* variables removed. The count is enough: the model needs to know a missing
|
|
98
|
+
* credential is deliberate and who to ask, and the names are the user's data.
|
|
99
|
+
* The kernel sends this as a notice of its own after the tool batch; a command's
|
|
100
|
+
* result carries only what the command printed.
|
|
101
|
+
*/
|
|
102
|
+
export function withheldEnvNoticeText(count: number): string {
|
|
103
|
+
if (count <= 0) return ''
|
|
104
|
+
const one = count === 1
|
|
105
|
+
return `A shell command that just ran had ${count} credential-shaped environment ${one ? 'variable' : 'variables'} removed from its environment, so ${one ? 'it reads' : 'they read'} as unset. When a failure looks like a missing credential, ask the user to provide ${one ? 'it' : 'one'}, because the user decides what a command may see.`
|
|
106
|
+
}
|
|
@@ -8,6 +8,8 @@ import { defineTool } from './defineTool.js'
|
|
|
8
8
|
* Read-only tools over an explicitly authorized historical source. The host
|
|
9
9
|
* resolves and checks the executing turn's scope; models choose no paths,
|
|
10
10
|
* tenant, resident, pursuit or upper-history boundary.
|
|
11
|
+
*
|
|
12
|
+
* @deprecated Residents are deprecated and removed in the next major release. Learning (`runLearningCycle` and the other `Learning*` names) keeps working, but `runLearningCycle` still takes a resident agenda until learning gets its own activation target in the next major release; for long-running work use scheduled tasks (`LocalTaskScheduler`), session goals and background agents.
|
|
11
13
|
*/
|
|
12
14
|
export function buildResidentHistoryTools(
|
|
13
15
|
resolveSource: (context: ToolContext) => ResidentHistorySource,
|
|
@@ -3,7 +3,11 @@ import type { ResidentToolEvidenceSource } from '../manager/resident/tool-eviden
|
|
|
3
3
|
import type { ToolContext, ToolDefinition } from '../types/tool/index.js'
|
|
4
4
|
import { defineTool } from './defineTool.js'
|
|
5
5
|
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* @experimental The host must authorize each executing turn before returning its bound source.
|
|
8
|
+
*
|
|
9
|
+
* @deprecated Residents are deprecated and removed in the next major release. Learning (`runLearningCycle` and the other `Learning*` names) keeps working, but `runLearningCycle` still takes a resident agenda until learning gets its own activation target in the next major release; for long-running work use scheduled tasks (`LocalTaskScheduler`), session goals and background agents.
|
|
10
|
+
*/
|
|
7
11
|
export function buildResidentToolEvidenceTools(
|
|
8
12
|
resolveSource: (context: ToolContext) => ResidentToolEvidenceSource,
|
|
9
13
|
): ToolDefinition[] {
|