@namzu/sdk 56.1.0 → 57.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 +54 -0
- package/dist/agents/QueryAgent.d.ts.map +1 -1
- package/dist/agents/QueryAgent.js +1 -0
- package/dist/agents/QueryAgent.js.map +1 -1
- package/dist/agents/index.d.ts +1 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +1 -0
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/plan.d.ts +20 -0
- package/dist/agents/plan.d.ts.map +1 -0
- package/dist/agents/plan.js +27 -0
- package/dist/agents/plan.js.map +1 -0
- package/dist/compaction/context-breakdown.d.ts +1 -1
- package/dist/compaction/context-breakdown.d.ts.map +1 -1
- package/dist/compaction/context-breakdown.js +9 -2
- package/dist/compaction/context-breakdown.js.map +1 -1
- package/dist/compaction/dangling.d.ts +12 -1
- package/dist/compaction/dangling.d.ts.map +1 -1
- package/dist/compaction/dangling.js +17 -3
- package/dist/compaction/dangling.js.map +1 -1
- package/dist/compaction/index.d.ts +2 -2
- package/dist/compaction/index.d.ts.map +1 -1
- package/dist/compaction/index.js.map +1 -1
- package/dist/compaction/manager.d.ts +16 -0
- package/dist/compaction/manager.d.ts.map +1 -1
- package/dist/compaction/manager.js +55 -0
- package/dist/compaction/manager.js.map +1 -1
- package/dist/compaction/manual.d.ts +2 -0
- package/dist/compaction/manual.d.ts.map +1 -1
- package/dist/compaction/manual.js +33 -4
- package/dist/compaction/manual.js.map +1 -1
- package/dist/compaction/restore.d.ts +51 -0
- package/dist/compaction/restore.d.ts.map +1 -0
- package/dist/compaction/restore.js +123 -0
- package/dist/compaction/restore.js.map +1 -0
- package/dist/compaction/types.d.ts +22 -0
- package/dist/compaction/types.d.ts.map +1 -1
- package/dist/compaction/verifier.d.ts.map +1 -1
- package/dist/compaction/verifier.js +3 -1
- package/dist/compaction/verifier.js.map +1 -1
- package/dist/compaction/wire.d.ts +3 -1
- package/dist/compaction/wire.d.ts.map +1 -1
- package/dist/compaction/wire.js +16 -0
- package/dist/compaction/wire.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/plugin/shell-hook.d.ts +19 -1
- package/dist/plugin/shell-hook.d.ts.map +1 -1
- package/dist/plugin/shell-hook.js +21 -5
- package/dist/plugin/shell-hook.js.map +1 -1
- package/dist/prompt/coding-agent-doctrine.d.ts +2 -2
- package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
- package/dist/prompt/coding-agent-doctrine.js +3 -2
- package/dist/prompt/coding-agent-doctrine.js.map +1 -1
- package/dist/provider/errors.d.ts +3 -0
- package/dist/provider/errors.d.ts.map +1 -1
- package/dist/provider/errors.js +23 -3
- package/dist/provider/errors.js.map +1 -1
- package/dist/provider/fallback.d.ts.map +1 -1
- package/dist/provider/fallback.js +34 -3
- package/dist/provider/fallback.js.map +1 -1
- package/dist/provider/retry.d.ts +12 -5
- package/dist/provider/retry.d.ts.map +1 -1
- package/dist/provider/retry.js +22 -8
- package/dist/provider/retry.js.map +1 -1
- package/dist/public-runtime.d.ts +5 -3
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +5 -2
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-types.d.ts +1 -1
- package/dist/public-types.d.ts.map +1 -1
- package/dist/runtime/jobs/registry.d.ts +42 -0
- package/dist/runtime/jobs/registry.d.ts.map +1 -1
- package/dist/runtime/jobs/registry.js +77 -0
- package/dist/runtime/jobs/registry.js.map +1 -1
- package/dist/runtime/query/calls-that-never-started.d.ts +12 -0
- package/dist/runtime/query/calls-that-never-started.d.ts.map +1 -0
- package/dist/runtime/query/calls-that-never-started.js +41 -0
- package/dist/runtime/query/calls-that-never-started.js.map +1 -0
- package/dist/runtime/query/executor/tool-call-admission.d.ts +2 -2
- package/dist/runtime/query/executor/tool-call-admission.d.ts.map +1 -1
- package/dist/runtime/query/executor/tool-call-admission.js +13 -9
- package/dist/runtime/query/executor/tool-call-admission.js.map +1 -1
- package/dist/runtime/query/executor.d.ts +16 -1
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +45 -2
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/file-evidence-context.d.ts +21 -4
- package/dist/runtime/query/file-evidence-context.d.ts.map +1 -1
- package/dist/runtime/query/file-evidence-context.js +70 -8
- package/dist/runtime/query/file-evidence-context.js.map +1 -1
- package/dist/runtime/query/index.d.ts +6 -0
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +7 -11
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts +1 -2
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +30 -19
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +20 -0
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts +2 -0
- package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/context.js.map +1 -1
- package/dist/runtime/query/iteration/recorded-context.d.ts +18 -0
- package/dist/runtime/query/iteration/recorded-context.d.ts.map +1 -0
- package/dist/runtime/query/iteration/recorded-context.js +37 -0
- package/dist/runtime/query/iteration/recorded-context.js.map +1 -0
- package/dist/runtime/query/iteration/step-context.d.ts +15 -51
- package/dist/runtime/query/iteration/step-context.d.ts.map +1 -1
- package/dist/runtime/query/iteration/step-context.js +24 -134
- package/dist/runtime/query/iteration/step-context.js.map +1 -1
- package/dist/runtime/query/iteration/step-shaping.d.ts +20 -8
- package/dist/runtime/query/iteration/step-shaping.d.ts.map +1 -1
- package/dist/runtime/query/iteration/step-shaping.js +52 -26
- package/dist/runtime/query/iteration/step-shaping.js.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.js +45 -25
- package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
- package/dist/runtime/query/prepare-turn.d.ts.map +1 -1
- package/dist/runtime/query/prepare-turn.js +10 -1
- package/dist/runtime/query/prepare-turn.js.map +1 -1
- package/dist/scheduler/completion-inbox.d.ts +27 -0
- package/dist/scheduler/completion-inbox.d.ts.map +1 -1
- package/dist/scheduler/completion-inbox.js +90 -23
- package/dist/scheduler/completion-inbox.js.map +1 -1
- package/dist/store/session-index/fts.d.ts.map +1 -1
- package/dist/store/session-index/fts.js +6 -0
- package/dist/store/session-index/fts.js.map +1 -1
- package/dist/test-support/file-evidence-held.d.ts +11 -0
- package/dist/test-support/file-evidence-held.d.ts.map +1 -0
- package/dist/test-support/file-evidence-held.js +28 -0
- package/dist/test-support/file-evidence-held.js.map +1 -0
- package/dist/test-support/request-only.d.ts +10 -0
- package/dist/test-support/request-only.d.ts.map +1 -0
- package/dist/test-support/request-only.js +15 -0
- package/dist/test-support/request-only.js.map +1 -0
- package/dist/tools/admit-facts.d.ts.map +1 -1
- package/dist/tools/admit-facts.js +1 -0
- package/dist/tools/admit-facts.js.map +1 -1
- package/dist/tools/builtins/bash.d.ts +42 -1
- package/dist/tools/builtins/bash.d.ts.map +1 -1
- package/dist/tools/builtins/bash.js +115 -48
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/builtins/read-file.d.ts.map +1 -1
- package/dist/tools/builtins/read-file.js +55 -5
- package/dist/tools/builtins/read-file.js.map +1 -1
- package/dist/tools/file-read-tracker.d.ts.map +1 -1
- package/dist/tools/file-read-tracker.js +28 -0
- package/dist/tools/file-read-tracker.js.map +1 -1
- package/dist/tools/untrusted-envelope.d.ts +7 -0
- package/dist/tools/untrusted-envelope.d.ts.map +1 -1
- package/dist/tools/untrusted-envelope.js +3 -1
- package/dist/tools/untrusted-envelope.js.map +1 -1
- package/dist/turn/evidence-recall.d.ts +19 -8
- package/dist/turn/evidence-recall.d.ts.map +1 -1
- package/dist/turn/evidence-recall.js +134 -53
- package/dist/turn/evidence-recall.js.map +1 -1
- package/dist/turn/memory-recall.d.ts +18 -0
- package/dist/turn/memory-recall.d.ts.map +1 -1
- package/dist/turn/memory-recall.js +43 -4
- package/dist/turn/memory-recall.js.map +1 -1
- package/dist/types/agent/base.d.ts +6 -0
- package/dist/types/agent/base.d.ts.map +1 -1
- package/dist/types/errors/catalog.d.ts +3 -0
- package/dist/types/errors/catalog.d.ts.map +1 -1
- package/dist/types/errors/catalog.js +24 -2
- package/dist/types/errors/catalog.js.map +1 -1
- package/dist/types/message/index.d.ts +46 -1
- package/dist/types/message/index.d.ts.map +1 -1
- package/dist/types/message/index.js +78 -1
- package/dist/types/message/index.js.map +1 -1
- package/dist/types/provider/error.d.ts +7 -0
- package/dist/types/provider/error.d.ts.map +1 -1
- package/dist/types/provider/errors.d.ts +24 -0
- package/dist/types/provider/errors.d.ts.map +1 -1
- package/dist/types/provider/errors.js +101 -1
- package/dist/types/provider/errors.js.map +1 -1
- package/dist/types/provider/stream.d.ts +6 -0
- package/dist/types/provider/stream.d.ts.map +1 -1
- package/dist/types/session/events.d.ts +8 -0
- package/dist/types/session/events.d.ts.map +1 -1
- package/dist/types/session/events.js.map +1 -1
- package/dist/types/session/prepare-step.d.ts +24 -0
- package/dist/types/session/prepare-step.d.ts.map +1 -1
- package/dist/types/tool/index.d.ts +39 -0
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/tool/index.js.map +1 -1
- package/dist/types/tool/presentation.d.ts +4 -0
- package/dist/types/tool/presentation.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/agents/QueryAgent.ts +1 -0
- package/src/agents/index.ts +1 -0
- package/src/agents/plan.ts +30 -0
- package/src/compaction/context-breakdown.ts +11 -3
- package/src/compaction/dangling.ts +35 -3
- package/src/compaction/index.ts +4 -0
- package/src/compaction/manager.ts +53 -0
- package/src/compaction/manual.ts +42 -10
- package/src/compaction/restore.ts +159 -0
- package/src/compaction/types.ts +25 -0
- package/src/compaction/verifier.ts +3 -1
- package/src/compaction/wire.ts +26 -1
- package/src/index.ts +2 -0
- package/src/plugin/shell-hook.ts +33 -5
- package/src/prompt/coding-agent-doctrine.ts +3 -2
- package/src/provider/errors.ts +27 -3
- package/src/provider/fallback.ts +34 -2
- package/src/provider/retry.ts +30 -10
- package/src/public-runtime.ts +15 -0
- package/src/public-types.ts +3 -0
- package/src/runtime/jobs/registry.ts +105 -0
- package/src/runtime/query/calls-that-never-started.ts +37 -0
- package/src/runtime/query/executor/tool-call-admission.ts +12 -9
- package/src/runtime/query/executor.ts +57 -2
- package/src/runtime/query/file-evidence-context.ts +79 -11
- package/src/runtime/query/index.ts +14 -10
- package/src/runtime/query/iteration/index.ts +32 -26
- package/src/runtime/query/iteration/phases/compaction.ts +19 -0
- package/src/runtime/query/iteration/phases/context.ts +3 -0
- package/src/runtime/query/iteration/recorded-context.ts +48 -0
- package/src/runtime/query/iteration/step-context.ts +27 -155
- package/src/runtime/query/iteration/step-shaping.ts +64 -40
- package/src/runtime/query/iteration/stream-turn.ts +47 -25
- package/src/runtime/query/prepare-turn.ts +10 -1
- package/src/scheduler/completion-inbox.ts +89 -22
- package/src/store/session-index/fts.ts +5 -0
- package/src/test-support/file-evidence-held.ts +27 -0
- package/src/test-support/request-only.ts +19 -0
- package/src/tools/admit-facts.ts +1 -0
- package/src/tools/builtins/bash.ts +149 -54
- package/src/tools/builtins/read-file.ts +66 -4
- package/src/tools/file-read-tracker.ts +25 -1
- package/src/tools/untrusted-envelope.ts +10 -1
- package/src/turn/evidence-recall.ts +189 -58
- package/src/turn/memory-recall.ts +59 -5
- package/src/types/agent/base.ts +7 -0
- package/src/types/errors/catalog.ts +31 -2
- package/src/types/message/index.ts +108 -1
- package/src/types/provider/error.ts +7 -0
- package/src/types/provider/errors.ts +120 -1
- package/src/types/provider/stream.ts +6 -0
- package/src/types/session/events.ts +8 -0
- package/src/types/session/prepare-step.ts +26 -0
- package/src/types/tool/index.ts +40 -0
- package/src/types/tool/presentation.ts +4 -0
|
@@ -371,6 +371,7 @@ export const RUNTIME_CONTEXT_MESSAGE_KINDS = [
|
|
|
371
371
|
'step-context',
|
|
372
372
|
'structured-output',
|
|
373
373
|
'task-completion',
|
|
374
|
+
'turn-context',
|
|
374
375
|
] as const
|
|
375
376
|
|
|
376
377
|
export type RuntimeContextMessageKind = (typeof RUNTIME_CONTEXT_MESSAGE_KINDS)[number]
|
|
@@ -381,8 +382,18 @@ export interface RuntimeContextMessageSource {
|
|
|
381
382
|
readonly kind: RuntimeContextMessageKind
|
|
382
383
|
/** Optional durable host delivery identity; it never grants operator authority. */
|
|
383
384
|
readonly deliveryRef?: InboundDeliveryRef
|
|
385
|
+
/**
|
|
386
|
+
* What a `turn-context` entry reports (for example `repository`). A later
|
|
387
|
+
* entry with the same key adds to or replaces what an earlier one said.
|
|
388
|
+
*/
|
|
389
|
+
readonly key?: string
|
|
384
390
|
}
|
|
385
391
|
|
|
392
|
+
/** The longest key a recorded context entry may carry. */
|
|
393
|
+
export const MAX_TURN_CONTEXT_KEY_LENGTH = 64
|
|
394
|
+
|
|
395
|
+
const TURN_CONTEXT_KEY = /^[a-z][a-z0-9-]*$/
|
|
396
|
+
|
|
386
397
|
export type UserMessageSource =
|
|
387
398
|
| GoalRoundMessageSource
|
|
388
399
|
| ProjectInstructionMessageSource
|
|
@@ -437,12 +448,14 @@ export function isRuntimeContextMessageSource(
|
|
|
437
448
|
readonly type?: unknown
|
|
438
449
|
readonly kind?: unknown
|
|
439
450
|
readonly deliveryRef?: unknown
|
|
451
|
+
readonly key?: unknown
|
|
440
452
|
}
|
|
441
453
|
return (
|
|
442
454
|
candidate.type === 'runtime-context' &&
|
|
443
455
|
typeof candidate.kind === 'string' &&
|
|
444
456
|
(RUNTIME_CONTEXT_MESSAGE_KINDS as readonly string[]).includes(candidate.kind) &&
|
|
445
|
-
(candidate.deliveryRef === undefined || isInboundDeliveryRef(candidate.deliveryRef))
|
|
457
|
+
(candidate.deliveryRef === undefined || isInboundDeliveryRef(candidate.deliveryRef)) &&
|
|
458
|
+
(candidate.key === undefined || isTurnContextKey(candidate.key))
|
|
446
459
|
)
|
|
447
460
|
}
|
|
448
461
|
|
|
@@ -640,6 +653,100 @@ export function createRuntimeContextMessage(
|
|
|
640
653
|
})
|
|
641
654
|
}
|
|
642
655
|
|
|
656
|
+
/** Whether `value` is a name a recorded context entry may be filed under. */
|
|
657
|
+
export function isTurnContextKey(value: unknown): value is string {
|
|
658
|
+
return (
|
|
659
|
+
typeof value === 'string' &&
|
|
660
|
+
value.length <= MAX_TURN_CONTEXT_KEY_LENGTH &&
|
|
661
|
+
TURN_CONTEXT_KEY.test(value)
|
|
662
|
+
)
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function localTime(at: Date): string {
|
|
666
|
+
const pad = (n: number) => String(n).padStart(2, '0')
|
|
667
|
+
return `${at.getFullYear()}-${pad(at.getMonth() + 1)}-${pad(at.getDate())} ${pad(at.getHours())}:${pad(at.getMinutes())}`
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Build a recorded context entry: text a step learned for this turn that
|
|
672
|
+
* stays in the conversation, after whatever came before it, so later requests
|
|
673
|
+
* keep beginning with it. The header is written into the content once and the
|
|
674
|
+
* entry is never edited afterwards; a later entry under the same key says what
|
|
675
|
+
* changed. It is not an operator message and carries no authority.
|
|
676
|
+
*
|
|
677
|
+
* The first entry under a key says that a later one adds to or replaces it;
|
|
678
|
+
* with `later` set, for an entry that follows another under the same key, the
|
|
679
|
+
* header leaves that sentence out, since the conversation already holds it.
|
|
680
|
+
*/
|
|
681
|
+
export function createTurnContextEntry(
|
|
682
|
+
key: string,
|
|
683
|
+
text: string,
|
|
684
|
+
at: Date = new Date(),
|
|
685
|
+
options: { readonly later?: boolean } = {},
|
|
686
|
+
): UserMessage {
|
|
687
|
+
if (!isTurnContextKey(key))
|
|
688
|
+
throw new TypeError('A recorded context key is lowercase words joined by hyphens.')
|
|
689
|
+
const label = `Recorded context (runtime-generated; not a new user request) - ${key}, as of ${localTime(at)}`
|
|
690
|
+
return createUserMessage(
|
|
691
|
+
`${label}${options.later ? ':' : '. A later entry with this key adds to or replaces what it says:'}\n${text}`,
|
|
692
|
+
undefined,
|
|
693
|
+
{ type: 'runtime-context', kind: 'turn-context', key },
|
|
694
|
+
)
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/** The key a recorded context entry is filed under, or undefined for any other message. */
|
|
698
|
+
export function turnContextKey(message: { readonly role: string; readonly source?: unknown }):
|
|
699
|
+
| string
|
|
700
|
+
| undefined {
|
|
701
|
+
if (message.role !== 'user') return undefined
|
|
702
|
+
const source = message.source as RuntimeContextMessageSource | undefined
|
|
703
|
+
return source?.type === 'runtime-context' && source.kind === 'turn-context'
|
|
704
|
+
? source.key
|
|
705
|
+
: undefined
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/** Whether `message` is a recorded context entry. */
|
|
709
|
+
export function isTurnContextEntry(message: {
|
|
710
|
+
readonly role: string
|
|
711
|
+
readonly source?: unknown
|
|
712
|
+
}): boolean {
|
|
713
|
+
if (message.role !== 'user') return false
|
|
714
|
+
const source = message.source as RuntimeContextMessageSource | undefined
|
|
715
|
+
return source?.type === 'runtime-context' && source.kind === 'turn-context'
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* What a step recorded under `key`, oldest first, without each entry's header.
|
|
720
|
+
* A history that was compacted holds only the entries the compaction kept, so
|
|
721
|
+
* an empty list means the step may say everything again.
|
|
722
|
+
*/
|
|
723
|
+
export function turnContextEntries(messages: readonly Message[], key: string): readonly string[] {
|
|
724
|
+
const bodies: string[] = []
|
|
725
|
+
for (const message of messages) {
|
|
726
|
+
if (turnContextKey(message) !== key || typeof message.content !== 'string') continue
|
|
727
|
+
bodies.push(message.content.slice(message.content.indexOf('\n') + 1))
|
|
728
|
+
}
|
|
729
|
+
return bodies
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* The text of the newest user-role message with plain text, skipping recorded
|
|
734
|
+
* context entries: a history that ends in one (a resumed session) must not
|
|
735
|
+
* take the host's own recalled text for the question.
|
|
736
|
+
*/
|
|
737
|
+
export function lastPromptText(messages: readonly Message[]): string {
|
|
738
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
739
|
+
const message = messages[i]
|
|
740
|
+
if (
|
|
741
|
+
message?.role === 'user' &&
|
|
742
|
+
typeof message.content === 'string' &&
|
|
743
|
+
!isTurnContextEntry(message)
|
|
744
|
+
)
|
|
745
|
+
return message.content
|
|
746
|
+
}
|
|
747
|
+
return ''
|
|
748
|
+
}
|
|
749
|
+
|
|
643
750
|
/** Build the retained user-context message that carries live project policy. */
|
|
644
751
|
export function createProjectInstructionMessage(
|
|
645
752
|
content: string,
|
|
@@ -51,4 +51,11 @@ export interface ProviderRequestErrorInit extends ProviderErrorInfo {
|
|
|
51
51
|
* is believed over the status code, so the retry loop leaves it alone.
|
|
52
52
|
*/
|
|
53
53
|
readonly shouldRetry?: boolean
|
|
54
|
+
/**
|
|
55
|
+
* Which part of the network path the request tripped on (a proxy that wants a
|
|
56
|
+
* sign-in, a certificate nobody trusts), when the vendor error's causes say.
|
|
57
|
+
* Data and not a `cause`: the raw error is never attached, but the diagnosis
|
|
58
|
+
* has to survive the wrapping or no one downstream can act on it.
|
|
59
|
+
*/
|
|
60
|
+
readonly transport?: import('./errors.js').TransportCause
|
|
54
61
|
}
|
|
@@ -278,6 +278,59 @@ function isUndiciTermination(err: unknown): boolean {
|
|
|
278
278
|
return false
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
+
/**
|
|
282
|
+
* Why a request never reached the model when the cause is the network path
|
|
283
|
+
* between this host and the provider, not the provider.
|
|
284
|
+
*
|
|
285
|
+
* - `proxy_auth_required`: a proxy answered the tunnel request asking for credentials.
|
|
286
|
+
* - `proxy_refused`: a proxy answered the tunnel request with any other refusal.
|
|
287
|
+
* - `untrusted_certificate`: the certificate on the connection did not chain to an
|
|
288
|
+
* authority this process trusts (typically a firewall that inspects encrypted traffic).
|
|
289
|
+
*/
|
|
290
|
+
export type TransportCause = 'proxy_auth_required' | 'proxy_refused' | 'untrusted_certificate'
|
|
291
|
+
|
|
292
|
+
const TRANSPORT_CAUSES: ReadonlySet<string> = new Set([
|
|
293
|
+
'proxy_auth_required',
|
|
294
|
+
'proxy_refused',
|
|
295
|
+
'untrusted_certificate',
|
|
296
|
+
])
|
|
297
|
+
|
|
298
|
+
const UNTRUSTED_CERTIFICATE_CODES: ReadonlySet<string> = new Set([
|
|
299
|
+
'UNABLE_TO_VERIFY_LEAF_SIGNATURE',
|
|
300
|
+
'UNABLE_TO_GET_ISSUER_CERT',
|
|
301
|
+
'UNABLE_TO_GET_ISSUER_CERT_LOCALLY',
|
|
302
|
+
'SELF_SIGNED_CERT_IN_CHAIN',
|
|
303
|
+
'DEPTH_ZERO_SELF_SIGNED_CERT',
|
|
304
|
+
'CERT_UNTRUSTED',
|
|
305
|
+
'CERT_CHAIN_TOO_LONG',
|
|
306
|
+
])
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Read a proxy refusal or an untrusted certificate off a failure's cause chain.
|
|
310
|
+
*
|
|
311
|
+
* Structural where it can be (an error code) and, for the proxy, the one
|
|
312
|
+
* sentence undici writes when a tunnel request is answered with a status other
|
|
313
|
+
* than 200, because that failure carries no code or status of its own.
|
|
314
|
+
*/
|
|
315
|
+
export function readTransportCause(err: unknown): TransportCause | undefined {
|
|
316
|
+
for (const link of causeChain(err)) {
|
|
317
|
+
// A driver's own error carries the diagnosis as data (see
|
|
318
|
+
// `ProviderRequestError.transport`) because it never keeps the cause.
|
|
319
|
+
if (typeof link.transport === 'string' && TRANSPORT_CAUSES.has(link.transport)) {
|
|
320
|
+
return link.transport as TransportCause
|
|
321
|
+
}
|
|
322
|
+
if (typeof link.code === 'string' && UNTRUSTED_CERTIFICATE_CODES.has(link.code)) {
|
|
323
|
+
return 'untrusted_certificate'
|
|
324
|
+
}
|
|
325
|
+
const tunnel =
|
|
326
|
+
typeof link.message === 'string'
|
|
327
|
+
? /Proxy response \((\d{3})\) !== 200 when HTTP Tunneling/.exec(link.message)
|
|
328
|
+
: null
|
|
329
|
+
if (tunnel) return tunnel[1] === '407' ? 'proxy_auth_required' : 'proxy_refused'
|
|
330
|
+
}
|
|
331
|
+
return undefined
|
|
332
|
+
}
|
|
333
|
+
|
|
281
334
|
/** Node/undici transport failures that mean "never reached the model". */
|
|
282
335
|
const NETWORK_ERRNOS: ReadonlySet<string> = new Set([
|
|
283
336
|
'ECONNRESET',
|
|
@@ -288,12 +341,54 @@ const NETWORK_ERRNOS: ReadonlySet<string> = new Set([
|
|
|
288
341
|
'ENETUNREACH',
|
|
289
342
|
'ENOTFOUND',
|
|
290
343
|
'EAI_AGAIN',
|
|
344
|
+
'ENETDOWN',
|
|
345
|
+
'EHOSTDOWN',
|
|
346
|
+
'ERR_PROXY_TUNNEL',
|
|
291
347
|
'EPROTO',
|
|
292
348
|
'UND_ERR_SOCKET',
|
|
349
|
+
'UND_ERR_CLOSED',
|
|
293
350
|
'UND_ERR_CONNECT_TIMEOUT',
|
|
294
351
|
'UND_ERR_RESPONSE_STATUS_CODE',
|
|
295
352
|
])
|
|
296
353
|
|
|
354
|
+
/**
|
|
355
|
+
* A certificate that fails verification. The peer was reached and answered,
|
|
356
|
+
* and resending the same request meets the same refusal, so it is a network
|
|
357
|
+
* failure that is not retried.
|
|
358
|
+
*/
|
|
359
|
+
const CERTIFICATE_ERRNOS: ReadonlySet<string> = new Set([
|
|
360
|
+
'UNABLE_TO_VERIFY_LEAF_SIGNATURE',
|
|
361
|
+
'UNABLE_TO_GET_ISSUER_CERT',
|
|
362
|
+
'UNABLE_TO_GET_ISSUER_CERT_LOCALLY',
|
|
363
|
+
'CERT_HAS_EXPIRED',
|
|
364
|
+
'CERT_NOT_YET_VALID',
|
|
365
|
+
'CERT_UNTRUSTED',
|
|
366
|
+
'CERT_REVOKED',
|
|
367
|
+
'DEPTH_ZERO_SELF_SIGNED_CERT',
|
|
368
|
+
'SELF_SIGNED_CERT_IN_CHAIN',
|
|
369
|
+
'ERR_TLS_CERT_ALTNAME_INVALID',
|
|
370
|
+
'HOSTNAME_MISMATCH',
|
|
371
|
+
])
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* The code of a certificate-verification failure anywhere on the chain, or
|
|
375
|
+
* `undefined`. Other TLS failures (a handshake cut short, a protocol error)
|
|
376
|
+
* are transient and are not this.
|
|
377
|
+
*/
|
|
378
|
+
export function readCertificateFailure(err: unknown): string | undefined {
|
|
379
|
+
for (const link of causeChain(err)) {
|
|
380
|
+
if (typeof link.code === 'string' && CERTIFICATE_ERRNOS.has(link.code)) return link.code
|
|
381
|
+
}
|
|
382
|
+
return undefined
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/** Other TLS and socket-layer codes that mean the connection broke, not that it was refused. */
|
|
386
|
+
function isTransientTransportCode(code: string): boolean {
|
|
387
|
+
return (
|
|
388
|
+
code.startsWith('ERR_SSL_') || (code.startsWith('ERR_TLS_') && !CERTIFICATE_ERRNOS.has(code))
|
|
389
|
+
)
|
|
390
|
+
}
|
|
391
|
+
|
|
297
392
|
const TIMEOUT_ERRNOS: ReadonlySet<string> = new Set([
|
|
298
393
|
'ETIMEDOUT',
|
|
299
394
|
'ESOCKETTIMEDOUT',
|
|
@@ -544,6 +639,20 @@ export function classifyProviderError(
|
|
|
544
639
|
})
|
|
545
640
|
}
|
|
546
641
|
|
|
642
|
+
// The network path itself: a proxy that wants credentials or refuses the
|
|
643
|
+
// tunnel, or a certificate nobody here trusts. Filed as `network` so it falls
|
|
644
|
+
// over like any unreachable provider, but never retried — the same request
|
|
645
|
+
// meets the same proxy or the same certificate again.
|
|
646
|
+
if (readTransportCause(err) !== undefined) {
|
|
647
|
+
return new ProviderError({
|
|
648
|
+
code: 'network',
|
|
649
|
+
message,
|
|
650
|
+
providerId,
|
|
651
|
+
retryable: false,
|
|
652
|
+
cause: err,
|
|
653
|
+
})
|
|
654
|
+
}
|
|
655
|
+
|
|
547
656
|
// A 400 that is really a window overflow must not be filed as a plain
|
|
548
657
|
// invalid_request: the caller can recover from one and not the other.
|
|
549
658
|
if (messages.some((m) => codeFromMessage(m, status) === 'context_length_exceeded')) {
|
|
@@ -570,6 +679,16 @@ export function classifyProviderError(
|
|
|
570
679
|
}
|
|
571
680
|
|
|
572
681
|
if (errno !== undefined) {
|
|
682
|
+
const certificate = readCertificateFailure(err)
|
|
683
|
+
if (certificate !== undefined) {
|
|
684
|
+
return new ProviderError({
|
|
685
|
+
code: 'network',
|
|
686
|
+
message,
|
|
687
|
+
providerId,
|
|
688
|
+
retryable: false,
|
|
689
|
+
cause: err,
|
|
690
|
+
})
|
|
691
|
+
}
|
|
573
692
|
if (TIMEOUT_ERRNOS.has(errno)) {
|
|
574
693
|
return new ProviderError({
|
|
575
694
|
code: 'timeout',
|
|
@@ -579,7 +698,7 @@ export function classifyProviderError(
|
|
|
579
698
|
cause: err,
|
|
580
699
|
})
|
|
581
700
|
}
|
|
582
|
-
if (NETWORK_ERRNOS.has(errno)) {
|
|
701
|
+
if (NETWORK_ERRNOS.has(errno) || isTransientTransportCode(errno)) {
|
|
583
702
|
return new ProviderError({
|
|
584
703
|
code: 'network',
|
|
585
704
|
message,
|
|
@@ -204,4 +204,10 @@ export interface ProviderFallbackNotice {
|
|
|
204
204
|
readonly status?: number
|
|
205
205
|
/** The classified failure's own sentence, already redacted at its source. */
|
|
206
206
|
readonly reason: string
|
|
207
|
+
/**
|
|
208
|
+
* The member that failed had streamed only pieces that can be taken back
|
|
209
|
+
* (reasoning, or a tool call still being written). The consumer drops them
|
|
210
|
+
* before the next chunk, as it does for a restarting retry.
|
|
211
|
+
*/
|
|
212
|
+
readonly restart?: boolean
|
|
207
213
|
}
|
|
@@ -447,6 +447,12 @@ type CoreSessionEvent =
|
|
|
447
447
|
status?: number
|
|
448
448
|
/** The classified failure's own sentence. */
|
|
449
449
|
reason: string
|
|
450
|
+
/**
|
|
451
|
+
* The member that failed had streamed only pieces that can be taken
|
|
452
|
+
* back (reasoning, or a tool call still being written): drop what this
|
|
453
|
+
* message showed so far, as for a restarting retry.
|
|
454
|
+
*/
|
|
455
|
+
restart?: boolean
|
|
450
456
|
}
|
|
451
457
|
| {
|
|
452
458
|
type: 'tool_completed'
|
|
@@ -800,6 +806,8 @@ type CoreSessionEvent =
|
|
|
800
806
|
duplicateToolResultsRemoved: number
|
|
801
807
|
orphanedToolResultsRemoved: number
|
|
802
808
|
syntheticToolResultsInserted: number
|
|
809
|
+
/** Of those, how many say the call never started. Absent when none. */
|
|
810
|
+
neverRanToolResultsInserted?: number
|
|
803
811
|
/** Exact number of durable image occurrences withheld from later requests. */
|
|
804
812
|
providerRejectedImagesSuppressed?: number
|
|
805
813
|
}
|
|
@@ -227,10 +227,36 @@ export interface PrepareStepResult {
|
|
|
227
227
|
*/
|
|
228
228
|
readonly context?: string
|
|
229
229
|
|
|
230
|
+
/**
|
|
231
|
+
* Text to keep in the conversation from this step on, filed under a key.
|
|
232
|
+
*
|
|
233
|
+
* Where `context` is gone after the request, an item here becomes a
|
|
234
|
+
* recorded entry appended after whatever the history ends with, so every
|
|
235
|
+
* later request (of this turn and of the next turns) begins with it and a
|
|
236
|
+
* caching provider keeps reading the same prefix. Entries are never edited
|
|
237
|
+
* or moved; a step that has something new says only that, under the same
|
|
238
|
+
* key, and reads what it said before with `turnContextEntries`. An item
|
|
239
|
+
* whose text is empty, or equal to the last entry under its key, adds
|
|
240
|
+
* nothing. Entries are ordinary history to compaction, which drops them
|
|
241
|
+
* with the messages around them, after which a step sees none and may say
|
|
242
|
+
* everything again.
|
|
243
|
+
*
|
|
244
|
+
* The model reads an entry as recorded context the host generated, not as
|
|
245
|
+
* something the operator said, and a person is never shown one.
|
|
246
|
+
*/
|
|
247
|
+
readonly recordedContext?: readonly RecordedContextItem[]
|
|
248
|
+
|
|
230
249
|
readonly temperature?: number
|
|
231
250
|
readonly maxResponseTokens?: number
|
|
232
251
|
}
|
|
233
252
|
|
|
253
|
+
/** One piece of text a step asks to have recorded, under what it reports. */
|
|
254
|
+
export interface RecordedContextItem {
|
|
255
|
+
/** Lowercase words joined by hyphens (for example `repository`). */
|
|
256
|
+
readonly key: string
|
|
257
|
+
readonly text: string
|
|
258
|
+
}
|
|
259
|
+
|
|
234
260
|
/**
|
|
235
261
|
* Called before each model call, with everything the turn has produced so
|
|
236
262
|
* far.
|
package/src/types/tool/index.ts
CHANGED
|
@@ -180,6 +180,18 @@ export interface BackgroundJobRegistryRef {
|
|
|
180
180
|
workingDirectory?: string
|
|
181
181
|
initialOutput?: readonly { readonly stream: 'stdout' | 'stderr'; readonly text: string }[]
|
|
182
182
|
}): { id: string; status: string }
|
|
183
|
+
/**
|
|
184
|
+
* Offer a foreground command this call is waiting on for hand-over to the
|
|
185
|
+
* background, until the returned function is called. `background` is asked
|
|
186
|
+
* to take the command over (`user`: the person asked; `message`: a message
|
|
187
|
+
* from the person must reach the model) and says whether it did. Optional:
|
|
188
|
+
* a host without it never moves a running command, and the call keeps the
|
|
189
|
+
* kill.
|
|
190
|
+
*/
|
|
191
|
+
registerForeground?(entry: {
|
|
192
|
+
command: string
|
|
193
|
+
background(reason: 'user' | 'message'): boolean
|
|
194
|
+
}): () => void
|
|
183
195
|
/**
|
|
184
196
|
* Resolves once the process exists or failed to start (`error` says why).
|
|
185
197
|
* Optional: a host that cannot tell may omit it, and `bash` then replies
|
|
@@ -341,6 +353,19 @@ export interface FileReadTracker {
|
|
|
341
353
|
* referencing a body it has been told is stale, without a check of its own.
|
|
342
354
|
*/
|
|
343
355
|
recordDriftObserved?(key: string): void
|
|
356
|
+
/**
|
|
357
|
+
* Optional note of the last read of one window of a file: the call whose
|
|
358
|
+
* receipt shows it, the fingerprint of that receipt, and the fingerprint of
|
|
359
|
+
* the whole body at the time.
|
|
360
|
+
*
|
|
361
|
+
* Lets a repeat read of the same window answer "unchanged" instead of
|
|
362
|
+
* returning the lines again. Only the read tool writes it, and it is only
|
|
363
|
+
* trusted together with {@link ToolContext.receiptVisible}: a receipt that
|
|
364
|
+
* has since been cleared, summarised away or edited is no answer.
|
|
365
|
+
*/
|
|
366
|
+
recordReadWindow?(key: string, window: string, entry: ReadWindowEntry): void
|
|
367
|
+
/** What {@link recordReadWindow} last noted for this window of this file. */
|
|
368
|
+
readWindow?(key: string, window: string): ReadWindowEntry | undefined
|
|
344
369
|
/** Whether a refused mutation has reported this path stale since the last observation. */
|
|
345
370
|
driftObserved?(key: string): boolean
|
|
346
371
|
/**
|
|
@@ -356,6 +381,15 @@ export interface FileReadTracker {
|
|
|
356
381
|
fingerprint?(key: string): string | undefined
|
|
357
382
|
}
|
|
358
383
|
|
|
384
|
+
/** The last read of one window of a file; see {@link FileReadTracker.recordReadWindow}. */
|
|
385
|
+
export interface ReadWindowEntry {
|
|
386
|
+
readonly callId: string
|
|
387
|
+
/** Fingerprint of the exact text the read returned. */
|
|
388
|
+
readonly renderedFingerprint: string
|
|
389
|
+
/** Fingerprint of the whole file body at that read. */
|
|
390
|
+
readonly contentFingerprint: string
|
|
391
|
+
}
|
|
392
|
+
|
|
359
393
|
export interface ToolPauseOption {
|
|
360
394
|
readonly id: string
|
|
361
395
|
readonly label: string
|
|
@@ -533,6 +567,12 @@ export interface ToolContext {
|
|
|
533
567
|
*/
|
|
534
568
|
codeAnalysis?: CodeAnalysisProvider
|
|
535
569
|
fileReadTracker?: FileReadTracker
|
|
570
|
+
/**
|
|
571
|
+
* Whether the receipt of an earlier call is still, byte for byte, in the
|
|
572
|
+
* conversation the model is shown. Absent when the host cannot say; a tool
|
|
573
|
+
* must then treat the answer as no.
|
|
574
|
+
*/
|
|
575
|
+
receiptVisible?: (callId: string, renderedFingerprint: string) => boolean
|
|
536
576
|
|
|
537
577
|
/**
|
|
538
578
|
* Where work that outlives this call is held.
|
|
@@ -151,6 +151,8 @@ export type ToolFacts =
|
|
|
151
151
|
readonly binary?: { readonly bytes: number }
|
|
152
152
|
/** The command was handed to a background job instead of being waited on. */
|
|
153
153
|
readonly background?: true
|
|
154
|
+
/** The command was already running when it was handed over (a key, a message, an interrupt or its timeout). */
|
|
155
|
+
readonly continued?: true
|
|
154
156
|
}
|
|
155
157
|
| {
|
|
156
158
|
readonly kind: 'read'
|
|
@@ -162,6 +164,8 @@ export type ToolFacts =
|
|
|
162
164
|
readonly returnedLines?: number
|
|
163
165
|
/** Only a window of the file was returned. */
|
|
164
166
|
readonly partial?: boolean
|
|
167
|
+
/** The lines were not repeated: an earlier read of the same lines still holds. */
|
|
168
|
+
readonly unchanged?: boolean
|
|
165
169
|
}
|
|
166
170
|
| {
|
|
167
171
|
readonly kind: 'search'
|