@jini-ai/daemon 0.1.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/LICENSE +201 -0
- package/dist/agent-executor.d.ts +458 -0
- package/dist/agent-executor.d.ts.map +1 -0
- package/dist/agent-executor.js +1277 -0
- package/dist/agent-executor.js.map +1 -0
- package/dist/artifacts/index.d.ts +14 -0
- package/dist/artifacts/index.d.ts.map +1 -0
- package/dist/artifacts/index.js +14 -0
- package/dist/artifacts/index.js.map +1 -0
- package/dist/artifacts/manifest.d.ts +90 -0
- package/dist/artifacts/manifest.d.ts.map +1 -0
- package/dist/artifacts/manifest.js +229 -0
- package/dist/artifacts/manifest.js.map +1 -0
- package/dist/artifacts/publication-guard.d.ts +28 -0
- package/dist/artifacts/publication-guard.d.ts.map +1 -0
- package/dist/artifacts/publication-guard.js +77 -0
- package/dist/artifacts/publication-guard.js.map +1 -0
- package/dist/artifacts/runtime-compat.d.ts +37 -0
- package/dist/artifacts/runtime-compat.d.ts.map +1 -0
- package/dist/artifacts/runtime-compat.js +33 -0
- package/dist/artifacts/runtime-compat.js.map +1 -0
- package/dist/artifacts/store.d.ts +85 -0
- package/dist/artifacts/store.d.ts.map +1 -0
- package/dist/artifacts/store.js +92 -0
- package/dist/artifacts/store.js.map +1 -0
- package/dist/artifacts/stub-guard.d.ts +73 -0
- package/dist/artifacts/stub-guard.d.ts.map +1 -0
- package/dist/artifacts/stub-guard.js +198 -0
- package/dist/artifacts/stub-guard.js.map +1 -0
- package/dist/artifacts/text-suppression.d.ts +68 -0
- package/dist/artifacts/text-suppression.d.ts.map +1 -0
- package/dist/artifacts/text-suppression.js +184 -0
- package/dist/artifacts/text-suppression.js.map +1 -0
- package/dist/close-status.d.ts +86 -0
- package/dist/close-status.d.ts.map +1 -0
- package/dist/close-status.js +91 -0
- package/dist/close-status.js.map +1 -0
- package/dist/continuation/continuation-transport.d.ts +41 -0
- package/dist/continuation/continuation-transport.d.ts.map +1 -0
- package/dist/continuation/continuation-transport.js +18 -0
- package/dist/continuation/continuation-transport.js.map +1 -0
- package/dist/continuation/index.d.ts +4 -0
- package/dist/continuation/index.d.ts.map +1 -0
- package/dist/continuation/index.js +4 -0
- package/dist/continuation/index.js.map +1 -0
- package/dist/continuation/journal.d.ts +37 -0
- package/dist/continuation/journal.d.ts.map +1 -0
- package/dist/continuation/journal.js +23 -0
- package/dist/continuation/journal.js.map +1 -0
- package/dist/continuation/run-start-handler.d.ts +75 -0
- package/dist/continuation/run-start-handler.d.ts.map +1 -0
- package/dist/continuation/run-start-handler.js +32 -0
- package/dist/continuation/run-start-handler.js.map +1 -0
- package/dist/delegated-tool-bridge.d.ts +53 -0
- package/dist/delegated-tool-bridge.d.ts.map +1 -0
- package/dist/delegated-tool-bridge.js +92 -0
- package/dist/delegated-tool-bridge.js.map +1 -0
- package/dist/event-log.d.ts +134 -0
- package/dist/event-log.d.ts.map +1 -0
- package/dist/event-log.js +108 -0
- package/dist/event-log.js.map +1 -0
- package/dist/frontend-capability-tools.d.ts +102 -0
- package/dist/frontend-capability-tools.d.ts.map +1 -0
- package/dist/frontend-capability-tools.js +68 -0
- package/dist/frontend-capability-tools.js.map +1 -0
- package/dist/frontend-session-registry.d.ts +127 -0
- package/dist/frontend-session-registry.d.ts.map +1 -0
- package/dist/frontend-session-registry.js +180 -0
- package/dist/frontend-session-registry.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/legacy-data-migration.d.ts +104 -0
- package/dist/legacy-data-migration.d.ts.map +1 -0
- package/dist/legacy-data-migration.js +239 -0
- package/dist/legacy-data-migration.js.map +1 -0
- package/dist/routines/index.d.ts +12 -0
- package/dist/routines/index.d.ts.map +1 -0
- package/dist/routines/index.js +12 -0
- package/dist/routines/index.js.map +1 -0
- package/dist/routines/routine-store.d.ts +77 -0
- package/dist/routines/routine-store.d.ts.map +1 -0
- package/dist/routines/routine-store.js +162 -0
- package/dist/routines/routine-store.js.map +1 -0
- package/dist/routines/schedule.d.ts +72 -0
- package/dist/routines/schedule.d.ts.map +1 -0
- package/dist/routines/schedule.js +263 -0
- package/dist/routines/schedule.js.map +1 -0
- package/dist/routines/scheduler.d.ts +34 -0
- package/dist/routines/scheduler.d.ts.map +1 -0
- package/dist/routines/scheduler.js +330 -0
- package/dist/routines/scheduler.js.map +1 -0
- package/dist/routines/types.d.ts +131 -0
- package/dist/routines/types.d.ts.map +1 -0
- package/dist/routines/types.js +9 -0
- package/dist/routines/types.js.map +1 -0
- package/dist/run/core/failure-taxonomy.d.ts +27 -0
- package/dist/run/core/failure-taxonomy.d.ts.map +1 -0
- package/dist/run/core/failure-taxonomy.js +16 -0
- package/dist/run/core/failure-taxonomy.js.map +1 -0
- package/dist/run/core/index.d.ts +5 -0
- package/dist/run/core/index.d.ts.map +1 -0
- package/dist/run/core/index.js +5 -0
- package/dist/run/core/index.js.map +1 -0
- package/dist/run/core/result.d.ts +25 -0
- package/dist/run/core/result.d.ts.map +1 -0
- package/dist/run/core/result.js +52 -0
- package/dist/run/core/result.js.map +1 -0
- package/dist/run/core/retry.d.ts +106 -0
- package/dist/run/core/retry.d.ts.map +1 -0
- package/dist/run/core/retry.js +210 -0
- package/dist/run/core/retry.js.map +1 -0
- package/dist/run/diagnostics/diagnostics.d.ts +86 -0
- package/dist/run/diagnostics/diagnostics.d.ts.map +1 -0
- package/dist/run/diagnostics/diagnostics.js +211 -0
- package/dist/run/diagnostics/diagnostics.js.map +1 -0
- package/dist/run/diagnostics/index.d.ts +3 -0
- package/dist/run/diagnostics/index.d.ts.map +1 -0
- package/dist/run/diagnostics/index.js +3 -0
- package/dist/run/diagnostics/index.js.map +1 -0
- package/dist/run/index.d.ts +16 -0
- package/dist/run/index.d.ts.map +1 -0
- package/dist/run/index.js +16 -0
- package/dist/run/index.js.map +1 -0
- package/dist/run-lifecycle.d.ts +118 -0
- package/dist/run-lifecycle.d.ts.map +1 -0
- package/dist/run-lifecycle.js +451 -0
- package/dist/run-lifecycle.js.map +1 -0
- package/dist/terminal-session.d.ts +139 -0
- package/dist/terminal-session.d.ts.map +1 -0
- package/dist/terminal-session.js +316 -0
- package/dist/terminal-session.js.map +1 -0
- package/dist/tokens.d.ts +9 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +19 -0
- package/dist/tokens.js.map +1 -0
- package/dist/tool-executor.d.ts +97 -0
- package/dist/tool-executor.d.ts.map +1 -0
- package/dist/tool-executor.js +198 -0
- package/dist/tool-executor.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain types for the routine scheduler ({@link ./scheduler.js}) and its HTTP-facing
|
|
3
|
+
* CRUD+history counterpart, {@link RoutineStore} (`./routine-store.js`). Mirrors OD's
|
|
4
|
+
* `apps/daemon/src/routines.ts` local type block field-for-field — that file's own header
|
|
5
|
+
* comment already noted these types are "a local mirror... kept here so this service
|
|
6
|
+
* typechecks under NodeNext," not a product coupling, so the port is a straight lift.
|
|
7
|
+
*/
|
|
8
|
+
export type RoutineRunStatus = 'queued' | 'running' | 'succeeded' | 'failed' | 'canceled';
|
|
9
|
+
export type RoutineRunTrigger = 'manual' | 'scheduled';
|
|
10
|
+
export type Weekday = 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
11
|
+
export type RoutineSchedule = {
|
|
12
|
+
kind: 'hourly';
|
|
13
|
+
minute: number;
|
|
14
|
+
} | {
|
|
15
|
+
kind: 'daily';
|
|
16
|
+
time: string;
|
|
17
|
+
timezone: string;
|
|
18
|
+
} | {
|
|
19
|
+
kind: 'weekdays';
|
|
20
|
+
time: string;
|
|
21
|
+
timezone: string;
|
|
22
|
+
} | {
|
|
23
|
+
kind: 'weekly';
|
|
24
|
+
time: string;
|
|
25
|
+
timezone: string;
|
|
26
|
+
weekday: Weekday;
|
|
27
|
+
};
|
|
28
|
+
export type RoutineProjectTarget = {
|
|
29
|
+
mode: 'create_each_run';
|
|
30
|
+
} | {
|
|
31
|
+
mode: 'reuse';
|
|
32
|
+
projectId: string;
|
|
33
|
+
};
|
|
34
|
+
/** Opaque scope selection a routine's run inherits — generic ids only, no OD skill/plugin/MCP/connector vocabulary baked in beyond field names already this abstract in the origin. */
|
|
35
|
+
export interface RoutineContextSelection {
|
|
36
|
+
skillIds?: string[];
|
|
37
|
+
pluginIds?: string[];
|
|
38
|
+
mcpServerIds?: string[];
|
|
39
|
+
connectorIds?: string[];
|
|
40
|
+
}
|
|
41
|
+
export interface Routine {
|
|
42
|
+
id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
prompt: string;
|
|
45
|
+
schedule: RoutineSchedule;
|
|
46
|
+
target: RoutineProjectTarget;
|
|
47
|
+
skillId: string | null;
|
|
48
|
+
agentId: string | null;
|
|
49
|
+
context: RoutineContextSelection;
|
|
50
|
+
enabled: boolean;
|
|
51
|
+
nextRunAt: number | null;
|
|
52
|
+
lastRun: unknown;
|
|
53
|
+
createdAt: number;
|
|
54
|
+
updatedAt: number;
|
|
55
|
+
}
|
|
56
|
+
export interface RoutineRun {
|
|
57
|
+
id: string;
|
|
58
|
+
routineId: string;
|
|
59
|
+
trigger: RoutineRunTrigger;
|
|
60
|
+
status: RoutineRunStatus;
|
|
61
|
+
projectId: string;
|
|
62
|
+
conversationId: string;
|
|
63
|
+
agentRunId: string;
|
|
64
|
+
startedAt: number;
|
|
65
|
+
completedAt: number | null;
|
|
66
|
+
summary: string | null;
|
|
67
|
+
error: string | null;
|
|
68
|
+
errorCode: string | null;
|
|
69
|
+
}
|
|
70
|
+
export interface RoutineRunHandlerStart {
|
|
71
|
+
projectId: string;
|
|
72
|
+
conversationId: string;
|
|
73
|
+
agentRunId: string;
|
|
74
|
+
completion: Promise<RoutineRunCompletion>;
|
|
75
|
+
prepare?: (run: RoutineRun) => void | Promise<void>;
|
|
76
|
+
start?: () => void;
|
|
77
|
+
/**
|
|
78
|
+
* Tear-down for the case where the handler returned a start handle but {@link RoutineService}
|
|
79
|
+
* later reached `prepare()` and it failed — i.e. the routine_run row exists, prepare may have
|
|
80
|
+
* partially mutated project/conversation/snapshot state, and the in-memory chat run still
|
|
81
|
+
* needs to terminate as `canceled`. Callers MUST surface failures rather than swallow them
|
|
82
|
+
* (the loser-retry path depends on it).
|
|
83
|
+
*/
|
|
84
|
+
discard?: () => void;
|
|
85
|
+
/**
|
|
86
|
+
* Tear-down for the case where the run was NEVER durably inserted — either `insertRun()`
|
|
87
|
+
* threw, or `insertRun()` returned `false` because a sibling daemon already won the scheduled
|
|
88
|
+
* slot. Prepare has not run, so no project/conversation/snapshot writes need rolling back. The
|
|
89
|
+
* in-memory chat run must also be removed from the registry instead of being finalized as
|
|
90
|
+
* `canceled`, otherwise duplicate-loser slots would surface phantom canceled runs on a run
|
|
91
|
+
* listing. Falls back to `discard` when the handler does not distinguish the two cases.
|
|
92
|
+
*/
|
|
93
|
+
discardUnstarted?: () => void;
|
|
94
|
+
}
|
|
95
|
+
export interface RoutineRunCompletion {
|
|
96
|
+
status: RoutineRunStatus;
|
|
97
|
+
summary?: string;
|
|
98
|
+
error?: string;
|
|
99
|
+
errorCode?: string | null;
|
|
100
|
+
}
|
|
101
|
+
export type RoutineRunHandler = (input: {
|
|
102
|
+
routine: Routine;
|
|
103
|
+
trigger: RoutineRunTrigger;
|
|
104
|
+
startedAt: number;
|
|
105
|
+
runId: string;
|
|
106
|
+
}) => Promise<RoutineRunHandlerStart>;
|
|
107
|
+
/**
|
|
108
|
+
* Storage port the scheduler ({@link ./scheduler.js}'s `RoutineService`) is injected with.
|
|
109
|
+
* Deliberately synchronous — kept faithful to the OD original rather than converted to this
|
|
110
|
+
* repo's usual async-port convention (see `event-log.ts`'s "ports are async-only from day one"
|
|
111
|
+
* doc note): the scheduler's `setTimeout`-driven fire path and its race-safe scheduled-slot
|
|
112
|
+
* claim (`ScheduledRunPersistenceError`) are exactly the "genuinely hard to get right" logic the
|
|
113
|
+
* porting proposal (`ADS-memory/reports/proposals/
|
|
114
|
+
* PROP-http-route-packs-automation-routines-2026-07-21.md`) flagged as "not something to
|
|
115
|
+
* casually reinvent" — a mechanical sync-to-async conversion of every call site inside
|
|
116
|
+
* `scheduleRoutineAt`'s timer callback risks introducing new races in exactly that logic for no
|
|
117
|
+
* behavioral gain, since a real backing store can still satisfy this port synchronously (an
|
|
118
|
+
* in-process cache kept warm by the separately-async {@link RoutineStore}) even when durable
|
|
119
|
+
* writes underneath are async. Building that bridge is host-level integration wiring, not this
|
|
120
|
+
* port's job — the same way `runs.ts`'s `onStarted` driver is host-supplied rather than built
|
|
121
|
+
* into `RunLifecycle` itself.
|
|
122
|
+
*/
|
|
123
|
+
export interface RoutinePersistence {
|
|
124
|
+
list(): Routine[];
|
|
125
|
+
insertRun(run: RoutineRun, options?: {
|
|
126
|
+
scheduledSlotAt?: number;
|
|
127
|
+
}): boolean | void;
|
|
128
|
+
updateRun(id: string, patch: Partial<RoutineRun>): void;
|
|
129
|
+
getLatestRun(routineId: string): RoutineRun | null;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/routines/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE1F,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEvD,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAEhD,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAEzE,MAAM,MAAM,oBAAoB,GAAG;IAAE,IAAI,EAAE,iBAAiB,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtG,uLAAuL;AACvL,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,eAAe,CAAC;IAC1B,MAAM,EAAE,oBAAoB,CAAC;IAC7B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,uBAAuB,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC1C,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,gBAAgB,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAC;AAEtC;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,IAAI,OAAO,EAAE,CAAC;IAClB,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,GAAG,IAAI,CAAC;IACnF,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;IACxD,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC;CACpD"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain types for the routine scheduler ({@link ./scheduler.js}) and its HTTP-facing
|
|
3
|
+
* CRUD+history counterpart, {@link RoutineStore} (`./routine-store.js`). Mirrors OD's
|
|
4
|
+
* `apps/daemon/src/routines.ts` local type block field-for-field — that file's own header
|
|
5
|
+
* comment already noted these types are "a local mirror... kept here so this service
|
|
6
|
+
* typechecks under NodeNext," not a product coupling, so the port is a straight lift.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/routines/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module run/core/failure-taxonomy — Neutral run-failure taxonomy.
|
|
3
|
+
*
|
|
4
|
+
* Product-neutral string-literal unions describing a run's failure
|
|
5
|
+
* classification (category / detail / stage / user action) and the safe-retry
|
|
6
|
+
* decision vocabulary (strategy / suppressed reason). The run-orchestration
|
|
7
|
+
* retry policy in `./retry.ts` consumes these as its failure-signal contract;
|
|
8
|
+
* a consumer supplies the concrete signal from its own classifier.
|
|
9
|
+
*
|
|
10
|
+
* These were the shared analytics-contract enums an external classifier stamped
|
|
11
|
+
* on each run. Only the vocabulary is kept here — the engine has no opinion on
|
|
12
|
+
* how a consumer derives it — with the single vendor-account-balance member
|
|
13
|
+
* dropped as product-specific.
|
|
14
|
+
*/
|
|
15
|
+
/** Terminal outcome of a run as reported to the retry policy. */
|
|
16
|
+
export type RunFailureResult = 'success' | 'failed' | 'cancelled';
|
|
17
|
+
/** Coarse failure family a terminal failure is bucketed into. */
|
|
18
|
+
export type RunFailureCategory = 'auth' | 'rate_limit' | 'insufficient_balance' | 'model_unavailable' | 'prompt_too_large' | 'upstream_unavailable' | 'timeout' | 'empty_output' | 'tool_error' | 'process_exit' | 'user_cancel' | 'unknown';
|
|
19
|
+
/** Specific failure detail refining a {@link RunFailureCategory}. */
|
|
20
|
+
export type RunFailureDetail = 'auth_required' | 'stale_profile' | 'refresh_token_reused' | 'missing_api_key' | 'invalid_api_key' | 'hard_quota' | 'workspace_credits_exhausted' | 'rate_limit_429' | 'model_not_found' | 'model_not_supported' | 'model_disabled' | 'local_model_not_loaded' | 'cli_version_incompatible' | 'prompt_too_large' | 'upstream_5xx' | 'upstream_client_error' | 'stream_disconnected' | 'network_error' | 'provider_high_demand' | 'provider_routing_error' | 'inactivity_timeout' | 'timeout' | 'empty_output' | 'tool_error' | 'plugin_artifact_missing' | 'cli_not_installed' | 'git_bash_missing' | 'agent_config_invalid' | 'spawn_failed' | 'spawn_enoexec' | 'spawn_ebadf' | 'spawn_eperm' | 'stdin_write_eof' | 'agent_protocol_error' | 'session_resume_expired' | 'fabricated_role_marker' | 'permission_request_not_found' | 'qoder_stop_sequence' | 'signal_killed' | 'process_crashed' | 'interrupted' | 'exit_code' | 'terminated_unknown' | 'stream_error' | 'exit_nonzero' | 'fatal_rpc_error' | 'execution_failed' | 'user_cancelled' | 'unknown';
|
|
21
|
+
/** Lifecycle stage a run had reached when it failed. */
|
|
22
|
+
export type RunFailureStage = 'preflight' | 'spawn' | 'session_init' | 'model_select' | 'prompt_send' | 'first_token_wait' | 'tool_execution' | 'artifact_write' | 'child_close' | 'finalize';
|
|
23
|
+
/** Retry strategy label attached to an automatic same-run transient retry. */
|
|
24
|
+
export type RunRetryStrategy = 'same_run_transient';
|
|
25
|
+
/** Reason an eligible-looking failure was NOT automatically retried. */
|
|
26
|
+
export type RunRetrySuppressedReason = 'not_failed' | 'not_retryable' | 'unsupported_category' | 'non_retryable_category' | 'unsafe_failure_stage' | 'missing_failure_signal' | 'hard_quota' | 'attempt_limit_reached' | 'cancel_requested' | 'user_visible_output_seen' | 'tool_call_seen' | 'artifact_write_seen' | 'live_artifact_seen';
|
|
27
|
+
//# sourceMappingURL=failure-taxonomy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failure-taxonomy.d.ts","sourceRoot":"","sources":["../../../src/run/core/failure-taxonomy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,iEAAiE;AACjE,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAC;AAElE,iEAAiE;AACjE,MAAM,MAAM,kBAAkB,GAC1B,MAAM,GACN,YAAY,GACZ,sBAAsB,GACtB,mBAAmB,GACnB,kBAAkB,GAClB,sBAAsB,GACtB,SAAS,GACT,cAAc,GACd,YAAY,GACZ,cAAc,GACd,aAAa,GACb,SAAS,CAAC;AAEd,qEAAqE;AACrE,MAAM,MAAM,gBAAgB,GACxB,eAAe,GACf,eAAe,GACf,sBAAsB,GACtB,iBAAiB,GACjB,iBAAiB,GACjB,YAAY,GACZ,6BAA6B,GAC7B,gBAAgB,GAChB,iBAAiB,GACjB,qBAAqB,GACrB,gBAAgB,GAChB,wBAAwB,GACxB,0BAA0B,GAC1B,kBAAkB,GAClB,cAAc,GACd,uBAAuB,GACvB,qBAAqB,GACrB,eAAe,GACf,sBAAsB,GACtB,wBAAwB,GACxB,oBAAoB,GACpB,SAAS,GACT,cAAc,GACd,YAAY,GACZ,yBAAyB,GACzB,mBAAmB,GACnB,kBAAkB,GAClB,sBAAsB,GACtB,cAAc,GACd,eAAe,GACf,aAAa,GACb,aAAa,GACb,iBAAiB,GACjB,sBAAsB,GACtB,wBAAwB,GACxB,wBAAwB,GACxB,8BAA8B,GAC9B,qBAAqB,GACrB,eAAe,GACf,iBAAiB,GACjB,aAAa,GACb,WAAW,GACX,oBAAoB,GACpB,cAAc,GACd,cAAc,GACd,iBAAiB,GACjB,kBAAkB,GAClB,gBAAgB,GAChB,SAAS,CAAC;AAEd,wDAAwD;AACxD,MAAM,MAAM,eAAe,GACvB,WAAW,GACX,OAAO,GACP,cAAc,GACd,cAAc,GACd,aAAa,GACb,kBAAkB,GAClB,gBAAgB,GAChB,gBAAgB,GAChB,aAAa,GACb,UAAU,CAAC;AAEf,8EAA8E;AAC9E,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,CAAC;AAEpD,wEAAwE;AACxE,MAAM,MAAM,wBAAwB,GAChC,YAAY,GACZ,eAAe,GACf,sBAAsB,GACtB,wBAAwB,GACxB,sBAAsB,GACtB,wBAAwB,GACxB,YAAY,GACZ,uBAAuB,GACvB,kBAAkB,GAClB,0BAA0B,GAC1B,gBAAgB,GAChB,qBAAqB,GACrB,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module run/core/failure-taxonomy — Neutral run-failure taxonomy.
|
|
3
|
+
*
|
|
4
|
+
* Product-neutral string-literal unions describing a run's failure
|
|
5
|
+
* classification (category / detail / stage / user action) and the safe-retry
|
|
6
|
+
* decision vocabulary (strategy / suppressed reason). The run-orchestration
|
|
7
|
+
* retry policy in `./retry.ts` consumes these as its failure-signal contract;
|
|
8
|
+
* a consumer supplies the concrete signal from its own classifier.
|
|
9
|
+
*
|
|
10
|
+
* These were the shared analytics-contract enums an external classifier stamped
|
|
11
|
+
* on each run. Only the vocabulary is kept here — the engine has no opinion on
|
|
12
|
+
* how a consumer derives it — with the single vendor-account-balance member
|
|
13
|
+
* dropped as product-specific.
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=failure-taxonomy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failure-taxonomy.js","sourceRoot":"","sources":["../../../src/run/core/failure-taxonomy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/run/core/index.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,cAAc,aAAa,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/run/core/index.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,cAAc,aAAa,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** @module run/core/result — Run result and status-code primitives shared by all run-domain concerns. */
|
|
2
|
+
/** Terminal outcome of a run as reported in analytics and lifecycle events. */
|
|
3
|
+
export type RunResult = 'success' | 'failed' | 'cancelled';
|
|
4
|
+
/** Minimal run-status shape required to derive analytics result and error codes. */
|
|
5
|
+
export interface RunStatusForAnalytics {
|
|
6
|
+
status: string;
|
|
7
|
+
errorCode?: string | null;
|
|
8
|
+
exitCode?: number | null;
|
|
9
|
+
signal?: string | null;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Maps a raw run status string to the canonical `RunResult` used by analytics.
|
|
13
|
+
* @param status - Raw status string from the run record (e.g. 'succeeded', 'canceled').
|
|
14
|
+
* @returns 'success', 'cancelled', or 'failed'.
|
|
15
|
+
*/
|
|
16
|
+
export declare function runResultFromStatus(status: string | undefined): RunResult;
|
|
17
|
+
/**
|
|
18
|
+
* Derives a non-empty `error_code` for analytics whenever `result === 'failed'`.
|
|
19
|
+
* Prefers the structured code stamped on the run; falls back to signal, exit
|
|
20
|
+
* code, or a sentinel so dashboards never see a blank cell.
|
|
21
|
+
* @param status - Run status fields including any stamped error code, exit code, and signal.
|
|
22
|
+
* @returns An error code string, or `undefined` when the run succeeded.
|
|
23
|
+
*/
|
|
24
|
+
export declare function deriveRunErrorCode(status: RunStatusForAnalytics): string | undefined;
|
|
25
|
+
//# sourceMappingURL=result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../../src/run/core/result.ts"],"names":[],"mappings":"AAYA,yGAAyG;AAEzG,+EAA+E;AAC/E,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE3D,oFAAoF;AACpF,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAIzE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,qBAAqB,GAC5B,MAAM,GAAG,SAAS,CAgBpB"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Turns a run's terminal status into the `result` + `error_code` shape that
|
|
2
|
+
// downstream analytics / lifecycle events expect.
|
|
3
|
+
//
|
|
4
|
+
// The invariant this enforces — `result === 'failed'` MUST carry a non-empty
|
|
5
|
+
// `error_code` — is exercisable in isolation. Several failure paths in the
|
|
6
|
+
// child-process lifecycle finish a run as `failed` directly without first
|
|
7
|
+
// emitting an `error` event (fatal RPC, stream-error fall-through, a child
|
|
8
|
+
// error with no diagnostic, etc.), leaving `errorCode === null`. The fallback
|
|
9
|
+
// chain below derives an `AGENT_SIGNAL_*` / `AGENT_EXIT_*` /
|
|
10
|
+
// `AGENT_TERMINATED_UNKNOWN` value for those cases so a failed run always
|
|
11
|
+
// carries an `error_code` and dashboards keyed on it never see a blank cell.
|
|
12
|
+
/**
|
|
13
|
+
* Maps a raw run status string to the canonical `RunResult` used by analytics.
|
|
14
|
+
* @param status - Raw status string from the run record (e.g. 'succeeded', 'canceled').
|
|
15
|
+
* @returns 'success', 'cancelled', or 'failed'.
|
|
16
|
+
*/
|
|
17
|
+
export function runResultFromStatus(status) {
|
|
18
|
+
if (status === 'succeeded')
|
|
19
|
+
return 'success';
|
|
20
|
+
if (status === 'canceled')
|
|
21
|
+
return 'cancelled';
|
|
22
|
+
return 'failed';
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Derives a non-empty `error_code` for analytics whenever `result === 'failed'`.
|
|
26
|
+
* Prefers the structured code stamped on the run; falls back to signal, exit
|
|
27
|
+
* code, or a sentinel so dashboards never see a blank cell.
|
|
28
|
+
* @param status - Run status fields including any stamped error code, exit code, and signal.
|
|
29
|
+
* @returns An error code string, or `undefined` when the run succeeded.
|
|
30
|
+
*/
|
|
31
|
+
export function deriveRunErrorCode(status) {
|
|
32
|
+
const result = runResultFromStatus(status.status);
|
|
33
|
+
if (result === 'success')
|
|
34
|
+
return undefined;
|
|
35
|
+
// Cancellation usually carries no error; only forward an explicit one
|
|
36
|
+
// when the daemon stamped it (e.g. cancel during error recovery).
|
|
37
|
+
if (result === 'cancelled')
|
|
38
|
+
return status.errorCode ?? undefined;
|
|
39
|
+
// Failure path: prefer the structured code stamped on the run. When the run
|
|
40
|
+
// reached `failed` without going through an `error` emission, derive the best
|
|
41
|
+
// signal we have.
|
|
42
|
+
const explicit = status.errorCode;
|
|
43
|
+
if (explicit)
|
|
44
|
+
return explicit;
|
|
45
|
+
if (status.signal)
|
|
46
|
+
return `AGENT_SIGNAL_${status.signal}`;
|
|
47
|
+
if (typeof status.exitCode === 'number' && status.exitCode !== 0) {
|
|
48
|
+
return `AGENT_EXIT_${status.exitCode}`;
|
|
49
|
+
}
|
|
50
|
+
return 'AGENT_TERMINATED_UNKNOWN';
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.js","sourceRoot":"","sources":["../../../src/run/core/result.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,0EAA0E;AAC1E,2EAA2E;AAC3E,8EAA8E;AAC9E,6DAA6D;AAC7D,0EAA0E;AAC1E,6EAA6E;AAe7E;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAA0B;IAC5D,IAAI,MAAM,KAAK,WAAW;QAAE,OAAO,SAAS,CAAC;IAC7C,IAAI,MAAM,KAAK,UAAU;QAAE,OAAO,WAAW,CAAC;IAC9C,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAA6B;IAE7B,MAAM,MAAM,GAAG,mBAAmB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAClD,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3C,sEAAsE;IACtE,kEAAkE;IAClE,IAAI,MAAM,KAAK,WAAW;QAAE,OAAO,MAAM,CAAC,SAAS,IAAI,SAAS,CAAC;IACjE,4EAA4E;IAC5E,8EAA8E;IAC9E,kBAAkB;IAClB,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC;IAClC,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,IAAI,MAAM,CAAC,MAAM;QAAE,OAAO,gBAAgB,MAAM,CAAC,MAAM,EAAE,CAAC;IAC1D,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QACjE,OAAO,cAAc,MAAM,CAAC,QAAQ,EAAE,CAAC;IACzC,CAAC;IACD,OAAO,0BAA0B,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { RunFailureCategory, RunFailureDetail, RunFailureStage, RunFailureResult, RunRetryStrategy, RunRetrySuppressedReason } from './failure-taxonomy.js';
|
|
2
|
+
/** @module run/core/retry — Retry backoff constants and policy decision logic for safe run retries. */
|
|
3
|
+
/** Maximum number of automatic same-run retry attempts allowed per run (not counting the initial attempt). */
|
|
4
|
+
export declare const DEFAULT_SAFE_RUN_RETRY_MAX_ATTEMPTS = 1;
|
|
5
|
+
/** Retry strategy label emitted in analytics for all automatic same-run transient retries. */
|
|
6
|
+
export declare const SAFE_RUN_RETRY_STRATEGY: RunRetryStrategy;
|
|
7
|
+
/** Base backoff delay in milliseconds for rate-limit retries; larger than the transient base because the upstream is explicitly asking us to slow down. */
|
|
8
|
+
export declare const RATE_LIMIT_RETRY_BASE_DELAY_MS = 1000;
|
|
9
|
+
/** Base backoff delay in milliseconds for non-rate-limit transient retries. */
|
|
10
|
+
export declare const TRANSIENT_RETRY_BASE_DELAY_MS = 500;
|
|
11
|
+
/** Exponential multiplier applied per retry attempt index to grow the backoff delay. */
|
|
12
|
+
export declare const RETRY_BACKOFF_MULTIPLIER = 2;
|
|
13
|
+
/** Maximum backoff delay in milliseconds after exponential growth and before jitter is applied. */
|
|
14
|
+
export declare const MAX_RETRY_BACKOFF_DELAY_MS = 8000;
|
|
15
|
+
/**
|
|
16
|
+
* Computes an exponentially backed-off delay with equal jitter for a retry attempt.
|
|
17
|
+
* @param attemptIndex - 1-based index of the attempt about to be scheduled.
|
|
18
|
+
* @param category - Failure category used to select the base delay; rate-limit failures use a larger base.
|
|
19
|
+
* @param random - Optional jitter source; defaults to `Math.random` for production use.
|
|
20
|
+
* @returns Backoff duration in milliseconds, capped at `MAX_RETRY_BACKOFF_DELAY_MS`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function computeRetryBackoffMs(attemptIndex: number, category: RunFailureCategory | undefined, random?: () => number): number;
|
|
23
|
+
/** Failure signal fields extracted from a run's error state, used by the retry policy. */
|
|
24
|
+
export interface RunRetryFailureSignal {
|
|
25
|
+
failure_category?: RunFailureCategory;
|
|
26
|
+
failure_detail?: RunFailureDetail;
|
|
27
|
+
failure_stage?: RunFailureStage;
|
|
28
|
+
retryable?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** Observable side-effects from the current run attempt that can suppress a retry to avoid double-work. */
|
|
31
|
+
export interface RunRetrySideEffectState {
|
|
32
|
+
cancelRequested?: boolean;
|
|
33
|
+
userVisibleOutputSeen?: boolean;
|
|
34
|
+
toolCallSeen?: boolean;
|
|
35
|
+
artifactWriteSeen?: boolean;
|
|
36
|
+
liveArtifactSeen?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/** Full input to `decideSafeRunRetry`, combining run outcome, failure signal, attempt count, and side-effect guards. */
|
|
39
|
+
export interface RunRetryPolicyInput {
|
|
40
|
+
result: RunFailureResult;
|
|
41
|
+
failure?: RunRetryFailureSignal;
|
|
42
|
+
attemptCount: number;
|
|
43
|
+
maxAttempts?: number;
|
|
44
|
+
sideEffects?: RunRetrySideEffectState;
|
|
45
|
+
random?: () => number;
|
|
46
|
+
}
|
|
47
|
+
/** Discriminated union output of `decideSafeRunRetry`; either a retry schedule or a suppression reason. */
|
|
48
|
+
export type RunRetryPolicyDecision = {
|
|
49
|
+
shouldRetry: true;
|
|
50
|
+
retryAttemptIndex: number;
|
|
51
|
+
retryMaxAttempts: number;
|
|
52
|
+
retryStrategy: RunRetryStrategy;
|
|
53
|
+
retryReason: 'transient_failure';
|
|
54
|
+
retryDelayMs: number;
|
|
55
|
+
} | {
|
|
56
|
+
shouldRetry: false;
|
|
57
|
+
retryAttemptIndex: number;
|
|
58
|
+
retryMaxAttempts: number;
|
|
59
|
+
retryStrategy: RunRetryStrategy;
|
|
60
|
+
retrySuppressedReason: RunRetrySuppressedReason;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Classifies a terminated child process's raw `code`/`signal` into the `process_exit` category
|
|
64
|
+
* this module's own policy understands — the only signal cheaply available to
|
|
65
|
+
* `@jini-ai/daemon`'s `ClassifyFailure` port (see `agent-executor.ts`'s `FailureClassificationContext`
|
|
66
|
+
* doc for why richer signal, like a detected 429 or protocol error, isn't available there without
|
|
67
|
+
* new stderr/stdout buffering machinery this pass deliberately does not add).
|
|
68
|
+
* @param code - The child process's exit code, or `null` if it was terminated by a signal.
|
|
69
|
+
* @param signal - The OS signal that terminated the child, or `null` if it exited normally.
|
|
70
|
+
* @returns A `RunRetryFailureSignal` suitable for {@link decideSafeRunRetry}.
|
|
71
|
+
* @complexity O(1).
|
|
72
|
+
*/
|
|
73
|
+
export declare function classifyProcessExitFailure(code: number | null, signal: string | null): RunRetryFailureSignal;
|
|
74
|
+
/**
|
|
75
|
+
* `@jini-ai/daemon`'s `ClassifyFailure` port needs a plain `boolean`, not a full
|
|
76
|
+
* {@link RunRetryPolicyDecision} — gap 4 only sets a failed run's `resumable` flag (informational
|
|
77
|
+
* metadata a host reads for its own later follow-up run, see `RunEndPayload.sessionRef`'s own doc),
|
|
78
|
+
* it does not schedule an automatic in-process retry the way {@link decideSafeRunRetry}'s full
|
|
79
|
+
* `attemptCount`/`sideEffects` machinery is built for. This composes the two: classifies the raw
|
|
80
|
+
* exit info via {@link classifyProcessExitFailure}, then asks {@link decideSafeRunRetry} whether a
|
|
81
|
+
* *first* attempt (`attemptCount: 0`) would be safe to retry, and returns that verdict.
|
|
82
|
+
*
|
|
83
|
+
* `attemptCount: 0` is always correct here, not a stand-in: no automatic same-run retry *loop*
|
|
84
|
+
* exists anywhere in this codebase (gap 4's `resumable` flag is read-only metadata for a host's own
|
|
85
|
+
* later follow-up run, never consumed to actually spawn attempt #2 — see this function's own doc
|
|
86
|
+
* above) — so every real call to this function genuinely is evaluating the first and only attempt
|
|
87
|
+
* made so far. A future auto-retry loop would need to supply its own real count, not reuse this.
|
|
88
|
+
*
|
|
89
|
+
* `sideEffects` (2026-07-22, optional — `agent-executor.ts`'s three `wire*Lifecycle` drivers supply
|
|
90
|
+
* `userVisibleOutputSeen`/`toolCallSeen`, derived live from the translated agent-event stream each
|
|
91
|
+
* already processes; see `FailureClassificationContext`'s own doc for why those two specifically,
|
|
92
|
+
* and why `cancelRequested`/`artifactWriteSeen`/`liveArtifactSeen` aren't threaded through this
|
|
93
|
+
* call at all) makes two of `decideSafeRunRetry`'s four side-effect-suppression guards genuinely
|
|
94
|
+
* exercised. Omitting it (`undefined`) preserves this function's original no-observed-side-effects
|
|
95
|
+
* behavior for any other caller.
|
|
96
|
+
* @complexity O(1).
|
|
97
|
+
*/
|
|
98
|
+
export declare function resumableFromProcessExit(code: number | null, signal: string | null, sideEffects?: Pick<RunRetrySideEffectState, 'userVisibleOutputSeen' | 'toolCallSeen'>): boolean;
|
|
99
|
+
/**
|
|
100
|
+
* Decides whether a failed run should be automatically retried and, if so, schedules the delay.
|
|
101
|
+
* Suppresses retries when the run was cancelled, emitted user-visible output, or hit a non-transient failure.
|
|
102
|
+
* @param input - Run outcome, failure classification, attempt count, and observable side effects.
|
|
103
|
+
* @returns A `RunRetryPolicyDecision` with `shouldRetry: true` and a delay, or `shouldRetry: false` with a suppression reason.
|
|
104
|
+
*/
|
|
105
|
+
export declare function decideSafeRunRetry(input: RunRetryPolicyInput): RunRetryPolicyDecision;
|
|
106
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../../src/run/core/retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,wBAAwB,EACzB,MAAM,uBAAuB,CAAC;AAE/B,uGAAuG;AAKvG,8GAA8G;AAC9G,eAAO,MAAM,mCAAmC,IAAI,CAAC;AACrD,8FAA8F;AAC9F,eAAO,MAAM,uBAAuB,EAAE,gBAAuC,CAAC;AAS9E,2JAA2J;AAC3J,eAAO,MAAM,8BAA8B,OAAQ,CAAC;AACpD,+EAA+E;AAC/E,eAAO,MAAM,6BAA6B,MAAM,CAAC;AACjD,wFAAwF;AACxF,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAC1C,mGAAmG;AACnG,eAAO,MAAM,0BAA0B,OAAQ,CAAC;AAYhD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,kBAAkB,GAAG,SAAS,EACxC,MAAM,GAAE,MAAM,MAAoB,GACjC,MAAM,CAQR;AAED,0FAA0F;AAC1F,MAAM,WAAW,qBAAqB;IACpC,gBAAgB,CAAC,EAAE,kBAAkB,CAAC;IACtC,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,aAAa,CAAC,EAAE,eAAe,CAAC;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,2GAA2G;AAC3G,MAAM,WAAW,uBAAuB;IACtC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,wHAAwH;AACxH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,gBAAgB,CAAC;IACzB,OAAO,CAAC,EAAE,qBAAqB,CAAC;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,uBAAuB,CAAC;IAEtC,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;CACvB;AAED,2GAA2G;AAC3G,MAAM,MAAM,sBAAsB,GAC9B;IACE,WAAW,EAAE,IAAI,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,gBAAgB,CAAC;IAChC,WAAW,EAAE,mBAAmB,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;CACtB,GACD;IACE,WAAW,EAAE,KAAK,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,gBAAgB,CAAC;IAChC,qBAAqB,EAAE,wBAAwB,CAAC;CACjD,CAAC;AAEN;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,qBAAqB,CAQ5G;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,GAAG,IAAI,EACnB,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,WAAW,CAAC,EAAE,IAAI,CAAC,uBAAuB,EAAE,uBAAuB,GAAG,cAAc,CAAC,GACpF,OAAO,CAQT;AA2DD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,mBAAmB,GACzB,sBAAsB,CAiDxB"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/** @module run/core/retry — Retry backoff constants and policy decision logic for safe run retries. */
|
|
2
|
+
// Counts automatic same-run retry attempts, not the initial run. The default
|
|
3
|
+
// scopes automatic recovery to at most one same-run retry, so `attemptCount >=
|
|
4
|
+
// 1` suppresses with `attempt_limit_reached`.
|
|
5
|
+
/** Maximum number of automatic same-run retry attempts allowed per run (not counting the initial attempt). */
|
|
6
|
+
export const DEFAULT_SAFE_RUN_RETRY_MAX_ATTEMPTS = 1;
|
|
7
|
+
/** Retry strategy label emitted in analytics for all automatic same-run transient retries. */
|
|
8
|
+
export const SAFE_RUN_RETRY_STRATEGY = 'same_run_transient';
|
|
9
|
+
// Backoff before a same-run retry restart. An immediate retry of a transient
|
|
10
|
+
// failure — especially a 429 — tends to re-hit the same limit, so the policy
|
|
11
|
+
// waits before restarting. `rate_limit` gets a larger base than other
|
|
12
|
+
// transient classes because the upstream is explicitly asking us to slow down.
|
|
13
|
+
// The delay grows exponentially by attempt index and is capped, then equal
|
|
14
|
+
// jitter (half fixed + half random) is applied to avoid synchronized retries
|
|
15
|
+
// across concurrent runs.
|
|
16
|
+
/** Base backoff delay in milliseconds for rate-limit retries; larger than the transient base because the upstream is explicitly asking us to slow down. */
|
|
17
|
+
export const RATE_LIMIT_RETRY_BASE_DELAY_MS = 1_000;
|
|
18
|
+
/** Base backoff delay in milliseconds for non-rate-limit transient retries. */
|
|
19
|
+
export const TRANSIENT_RETRY_BASE_DELAY_MS = 500;
|
|
20
|
+
/** Exponential multiplier applied per retry attempt index to grow the backoff delay. */
|
|
21
|
+
export const RETRY_BACKOFF_MULTIPLIER = 2;
|
|
22
|
+
/** Maximum backoff delay in milliseconds after exponential growth and before jitter is applied. */
|
|
23
|
+
export const MAX_RETRY_BACKOFF_DELAY_MS = 8_000;
|
|
24
|
+
function backoffBaseDelayMs(category) {
|
|
25
|
+
return category === 'rate_limit'
|
|
26
|
+
? RATE_LIMIT_RETRY_BASE_DELAY_MS
|
|
27
|
+
: TRANSIENT_RETRY_BASE_DELAY_MS;
|
|
28
|
+
}
|
|
29
|
+
// Pure, deterministic when `random` is supplied. `attemptIndex` is 1-based (the
|
|
30
|
+
// index of the attempt about to be scheduled), so the first retry uses the base
|
|
31
|
+
// delay and each subsequent attempt doubles it up to the cap. Equal jitter
|
|
32
|
+
// returns a value in [delay/2, delay].
|
|
33
|
+
/**
|
|
34
|
+
* Computes an exponentially backed-off delay with equal jitter for a retry attempt.
|
|
35
|
+
* @param attemptIndex - 1-based index of the attempt about to be scheduled.
|
|
36
|
+
* @param category - Failure category used to select the base delay; rate-limit failures use a larger base.
|
|
37
|
+
* @param random - Optional jitter source; defaults to `Math.random` for production use.
|
|
38
|
+
* @returns Backoff duration in milliseconds, capped at `MAX_RETRY_BACKOFF_DELAY_MS`.
|
|
39
|
+
*/
|
|
40
|
+
export function computeRetryBackoffMs(attemptIndex, category, random = Math.random) {
|
|
41
|
+
const exponent = Math.max(0, Math.floor(attemptIndex) - 1);
|
|
42
|
+
const raw = backoffBaseDelayMs(category) * RETRY_BACKOFF_MULTIPLIER ** exponent;
|
|
43
|
+
const capped = Math.min(raw, MAX_RETRY_BACKOFF_DELAY_MS);
|
|
44
|
+
const half = capped / 2;
|
|
45
|
+
const sample = random();
|
|
46
|
+
const jitter = Number.isFinite(sample) ? Math.min(1, Math.max(0, sample)) : 0;
|
|
47
|
+
return Math.round(half + jitter * half);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Classifies a terminated child process's raw `code`/`signal` into the `process_exit` category
|
|
51
|
+
* this module's own policy understands — the only signal cheaply available to
|
|
52
|
+
* `@jini-ai/daemon`'s `ClassifyFailure` port (see `agent-executor.ts`'s `FailureClassificationContext`
|
|
53
|
+
* doc for why richer signal, like a detected 429 or protocol error, isn't available there without
|
|
54
|
+
* new stderr/stdout buffering machinery this pass deliberately does not add).
|
|
55
|
+
* @param code - The child process's exit code, or `null` if it was terminated by a signal.
|
|
56
|
+
* @param signal - The OS signal that terminated the child, or `null` if it exited normally.
|
|
57
|
+
* @returns A `RunRetryFailureSignal` suitable for {@link decideSafeRunRetry}.
|
|
58
|
+
* @complexity O(1).
|
|
59
|
+
*/
|
|
60
|
+
export function classifyProcessExitFailure(code, signal) {
|
|
61
|
+
if (signal !== null) {
|
|
62
|
+
return { failure_category: 'process_exit', failure_detail: 'signal_killed', retryable: true };
|
|
63
|
+
}
|
|
64
|
+
if (code !== null && code !== 0) {
|
|
65
|
+
return { failure_category: 'process_exit', failure_detail: 'exit_nonzero', retryable: false };
|
|
66
|
+
}
|
|
67
|
+
return { failure_category: 'process_exit', failure_detail: 'terminated_unknown', retryable: false };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* `@jini-ai/daemon`'s `ClassifyFailure` port needs a plain `boolean`, not a full
|
|
71
|
+
* {@link RunRetryPolicyDecision} — gap 4 only sets a failed run's `resumable` flag (informational
|
|
72
|
+
* metadata a host reads for its own later follow-up run, see `RunEndPayload.sessionRef`'s own doc),
|
|
73
|
+
* it does not schedule an automatic in-process retry the way {@link decideSafeRunRetry}'s full
|
|
74
|
+
* `attemptCount`/`sideEffects` machinery is built for. This composes the two: classifies the raw
|
|
75
|
+
* exit info via {@link classifyProcessExitFailure}, then asks {@link decideSafeRunRetry} whether a
|
|
76
|
+
* *first* attempt (`attemptCount: 0`) would be safe to retry, and returns that verdict.
|
|
77
|
+
*
|
|
78
|
+
* `attemptCount: 0` is always correct here, not a stand-in: no automatic same-run retry *loop*
|
|
79
|
+
* exists anywhere in this codebase (gap 4's `resumable` flag is read-only metadata for a host's own
|
|
80
|
+
* later follow-up run, never consumed to actually spawn attempt #2 — see this function's own doc
|
|
81
|
+
* above) — so every real call to this function genuinely is evaluating the first and only attempt
|
|
82
|
+
* made so far. A future auto-retry loop would need to supply its own real count, not reuse this.
|
|
83
|
+
*
|
|
84
|
+
* `sideEffects` (2026-07-22, optional — `agent-executor.ts`'s three `wire*Lifecycle` drivers supply
|
|
85
|
+
* `userVisibleOutputSeen`/`toolCallSeen`, derived live from the translated agent-event stream each
|
|
86
|
+
* already processes; see `FailureClassificationContext`'s own doc for why those two specifically,
|
|
87
|
+
* and why `cancelRequested`/`artifactWriteSeen`/`liveArtifactSeen` aren't threaded through this
|
|
88
|
+
* call at all) makes two of `decideSafeRunRetry`'s four side-effect-suppression guards genuinely
|
|
89
|
+
* exercised. Omitting it (`undefined`) preserves this function's original no-observed-side-effects
|
|
90
|
+
* behavior for any other caller.
|
|
91
|
+
* @complexity O(1).
|
|
92
|
+
*/
|
|
93
|
+
export function resumableFromProcessExit(code, signal, sideEffects) {
|
|
94
|
+
const decision = decideSafeRunRetry({
|
|
95
|
+
result: 'failed',
|
|
96
|
+
failure: classifyProcessExitFailure(code, signal),
|
|
97
|
+
attemptCount: 0,
|
|
98
|
+
...(sideEffects !== undefined ? { sideEffects } : {}),
|
|
99
|
+
});
|
|
100
|
+
return decision.shouldRetry;
|
|
101
|
+
}
|
|
102
|
+
function normalizeAttemptCount(attemptCount) {
|
|
103
|
+
if (!Number.isFinite(attemptCount) || attemptCount < 0)
|
|
104
|
+
return 0;
|
|
105
|
+
return Math.floor(attemptCount);
|
|
106
|
+
}
|
|
107
|
+
function normalizeMaxAttempts(maxAttempts) {
|
|
108
|
+
if (maxAttempts === undefined)
|
|
109
|
+
return DEFAULT_SAFE_RUN_RETRY_MAX_ATTEMPTS;
|
|
110
|
+
if (!Number.isFinite(maxAttempts) || maxAttempts < 0)
|
|
111
|
+
return 0;
|
|
112
|
+
return Math.floor(maxAttempts);
|
|
113
|
+
}
|
|
114
|
+
function transientSuppressedReason(category, detail, stage) {
|
|
115
|
+
if (category === undefined)
|
|
116
|
+
return 'missing_failure_signal';
|
|
117
|
+
if (category === 'rate_limit') {
|
|
118
|
+
return detail === 'rate_limit_429' ? null : 'non_retryable_category';
|
|
119
|
+
}
|
|
120
|
+
if (category === 'upstream_unavailable') {
|
|
121
|
+
return detail === 'stream_disconnected' ||
|
|
122
|
+
detail === 'upstream_5xx' ||
|
|
123
|
+
detail === 'provider_high_demand' ||
|
|
124
|
+
detail === 'provider_routing_error' ||
|
|
125
|
+
detail === 'network_error'
|
|
126
|
+
? null
|
|
127
|
+
: 'non_retryable_category';
|
|
128
|
+
}
|
|
129
|
+
if (category === 'empty_output') {
|
|
130
|
+
return stage === undefined || stage === 'first_token_wait'
|
|
131
|
+
? null
|
|
132
|
+
: 'unsafe_failure_stage';
|
|
133
|
+
}
|
|
134
|
+
if (category === 'timeout') {
|
|
135
|
+
return stage === 'first_token_wait'
|
|
136
|
+
? null
|
|
137
|
+
: 'unsafe_failure_stage';
|
|
138
|
+
}
|
|
139
|
+
if (category === 'process_exit') {
|
|
140
|
+
// `signal_killed` added 2026-07-22 (gap 4's real default classifier — see `classifyProcessExitFailure`
|
|
141
|
+
// below): a process terminated by an OS signal (SIGKILL/SIGTERM/etc) was never the agent's own
|
|
142
|
+
// choice to fail — an OOM-kill or an infra-level eviction is the common real-world cause, and
|
|
143
|
+
// both are presumptively transient. A plain non-zero exit code, by contrast, is the agent's own
|
|
144
|
+
// process deciding to fail (a config problem, a deterministic bug) and is not retried here.
|
|
145
|
+
return detail === 'agent_protocol_error' ||
|
|
146
|
+
detail === 'qoder_stop_sequence' ||
|
|
147
|
+
detail === 'session_resume_expired' ||
|
|
148
|
+
detail === 'stream_error' ||
|
|
149
|
+
detail === 'fatal_rpc_error' ||
|
|
150
|
+
detail === 'signal_killed'
|
|
151
|
+
? null
|
|
152
|
+
: 'non_retryable_category';
|
|
153
|
+
}
|
|
154
|
+
return 'non_retryable_category';
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Decides whether a failed run should be automatically retried and, if so, schedules the delay.
|
|
158
|
+
* Suppresses retries when the run was cancelled, emitted user-visible output, or hit a non-transient failure.
|
|
159
|
+
* @param input - Run outcome, failure classification, attempt count, and observable side effects.
|
|
160
|
+
* @returns A `RunRetryPolicyDecision` with `shouldRetry: true` and a delay, or `shouldRetry: false` with a suppression reason.
|
|
161
|
+
*/
|
|
162
|
+
export function decideSafeRunRetry(input) {
|
|
163
|
+
const attemptCount = normalizeAttemptCount(input.attemptCount);
|
|
164
|
+
const retryMaxAttempts = normalizeMaxAttempts(input.maxAttempts);
|
|
165
|
+
const retryAttemptIndex = attemptCount + 1;
|
|
166
|
+
const base = {
|
|
167
|
+
retryAttemptIndex,
|
|
168
|
+
retryMaxAttempts,
|
|
169
|
+
retryStrategy: SAFE_RUN_RETRY_STRATEGY,
|
|
170
|
+
};
|
|
171
|
+
const suppress = (retrySuppressedReason) => ({
|
|
172
|
+
...base,
|
|
173
|
+
shouldRetry: false,
|
|
174
|
+
retrySuppressedReason,
|
|
175
|
+
});
|
|
176
|
+
if (input.result !== 'failed')
|
|
177
|
+
return suppress('not_failed');
|
|
178
|
+
const sideEffects = input.sideEffects ?? {};
|
|
179
|
+
if (sideEffects.cancelRequested)
|
|
180
|
+
return suppress('cancel_requested');
|
|
181
|
+
const failure = input.failure;
|
|
182
|
+
if (!failure)
|
|
183
|
+
return suppress('missing_failure_signal');
|
|
184
|
+
if (failure.failure_detail === 'hard_quota')
|
|
185
|
+
return suppress('hard_quota');
|
|
186
|
+
const transientReason = transientSuppressedReason(failure.failure_category, failure.failure_detail, failure.failure_stage);
|
|
187
|
+
if (transientReason === 'non_retryable_category')
|
|
188
|
+
return suppress(transientReason);
|
|
189
|
+
if (!failure.retryable)
|
|
190
|
+
return suppress('not_retryable');
|
|
191
|
+
if (transientReason)
|
|
192
|
+
return suppress(transientReason);
|
|
193
|
+
if (attemptCount >= retryMaxAttempts)
|
|
194
|
+
return suppress('attempt_limit_reached');
|
|
195
|
+
if (sideEffects.userVisibleOutputSeen)
|
|
196
|
+
return suppress('user_visible_output_seen');
|
|
197
|
+
if (sideEffects.toolCallSeen)
|
|
198
|
+
return suppress('tool_call_seen');
|
|
199
|
+
if (sideEffects.artifactWriteSeen)
|
|
200
|
+
return suppress('artifact_write_seen');
|
|
201
|
+
if (sideEffects.liveArtifactSeen)
|
|
202
|
+
return suppress('live_artifact_seen');
|
|
203
|
+
return {
|
|
204
|
+
...base,
|
|
205
|
+
shouldRetry: true,
|
|
206
|
+
retryReason: 'transient_failure',
|
|
207
|
+
retryDelayMs: computeRetryBackoffMs(retryAttemptIndex, failure.failure_category, input.random),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=retry.js.map
|