@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.
Files changed (142) hide show
  1. package/LICENSE +201 -0
  2. package/dist/agent-executor.d.ts +458 -0
  3. package/dist/agent-executor.d.ts.map +1 -0
  4. package/dist/agent-executor.js +1277 -0
  5. package/dist/agent-executor.js.map +1 -0
  6. package/dist/artifacts/index.d.ts +14 -0
  7. package/dist/artifacts/index.d.ts.map +1 -0
  8. package/dist/artifacts/index.js +14 -0
  9. package/dist/artifacts/index.js.map +1 -0
  10. package/dist/artifacts/manifest.d.ts +90 -0
  11. package/dist/artifacts/manifest.d.ts.map +1 -0
  12. package/dist/artifacts/manifest.js +229 -0
  13. package/dist/artifacts/manifest.js.map +1 -0
  14. package/dist/artifacts/publication-guard.d.ts +28 -0
  15. package/dist/artifacts/publication-guard.d.ts.map +1 -0
  16. package/dist/artifacts/publication-guard.js +77 -0
  17. package/dist/artifacts/publication-guard.js.map +1 -0
  18. package/dist/artifacts/runtime-compat.d.ts +37 -0
  19. package/dist/artifacts/runtime-compat.d.ts.map +1 -0
  20. package/dist/artifacts/runtime-compat.js +33 -0
  21. package/dist/artifacts/runtime-compat.js.map +1 -0
  22. package/dist/artifacts/store.d.ts +85 -0
  23. package/dist/artifacts/store.d.ts.map +1 -0
  24. package/dist/artifacts/store.js +92 -0
  25. package/dist/artifacts/store.js.map +1 -0
  26. package/dist/artifacts/stub-guard.d.ts +73 -0
  27. package/dist/artifacts/stub-guard.d.ts.map +1 -0
  28. package/dist/artifacts/stub-guard.js +198 -0
  29. package/dist/artifacts/stub-guard.js.map +1 -0
  30. package/dist/artifacts/text-suppression.d.ts +68 -0
  31. package/dist/artifacts/text-suppression.d.ts.map +1 -0
  32. package/dist/artifacts/text-suppression.js +184 -0
  33. package/dist/artifacts/text-suppression.js.map +1 -0
  34. package/dist/close-status.d.ts +86 -0
  35. package/dist/close-status.d.ts.map +1 -0
  36. package/dist/close-status.js +91 -0
  37. package/dist/close-status.js.map +1 -0
  38. package/dist/continuation/continuation-transport.d.ts +41 -0
  39. package/dist/continuation/continuation-transport.d.ts.map +1 -0
  40. package/dist/continuation/continuation-transport.js +18 -0
  41. package/dist/continuation/continuation-transport.js.map +1 -0
  42. package/dist/continuation/index.d.ts +4 -0
  43. package/dist/continuation/index.d.ts.map +1 -0
  44. package/dist/continuation/index.js +4 -0
  45. package/dist/continuation/index.js.map +1 -0
  46. package/dist/continuation/journal.d.ts +37 -0
  47. package/dist/continuation/journal.d.ts.map +1 -0
  48. package/dist/continuation/journal.js +23 -0
  49. package/dist/continuation/journal.js.map +1 -0
  50. package/dist/continuation/run-start-handler.d.ts +75 -0
  51. package/dist/continuation/run-start-handler.d.ts.map +1 -0
  52. package/dist/continuation/run-start-handler.js +32 -0
  53. package/dist/continuation/run-start-handler.js.map +1 -0
  54. package/dist/delegated-tool-bridge.d.ts +53 -0
  55. package/dist/delegated-tool-bridge.d.ts.map +1 -0
  56. package/dist/delegated-tool-bridge.js +92 -0
  57. package/dist/delegated-tool-bridge.js.map +1 -0
  58. package/dist/event-log.d.ts +134 -0
  59. package/dist/event-log.d.ts.map +1 -0
  60. package/dist/event-log.js +108 -0
  61. package/dist/event-log.js.map +1 -0
  62. package/dist/frontend-capability-tools.d.ts +102 -0
  63. package/dist/frontend-capability-tools.d.ts.map +1 -0
  64. package/dist/frontend-capability-tools.js +68 -0
  65. package/dist/frontend-capability-tools.js.map +1 -0
  66. package/dist/frontend-session-registry.d.ts +127 -0
  67. package/dist/frontend-session-registry.d.ts.map +1 -0
  68. package/dist/frontend-session-registry.js +180 -0
  69. package/dist/frontend-session-registry.js.map +1 -0
  70. package/dist/index.d.ts +38 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +38 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/legacy-data-migration.d.ts +104 -0
  75. package/dist/legacy-data-migration.d.ts.map +1 -0
  76. package/dist/legacy-data-migration.js +239 -0
  77. package/dist/legacy-data-migration.js.map +1 -0
  78. package/dist/routines/index.d.ts +12 -0
  79. package/dist/routines/index.d.ts.map +1 -0
  80. package/dist/routines/index.js +12 -0
  81. package/dist/routines/index.js.map +1 -0
  82. package/dist/routines/routine-store.d.ts +77 -0
  83. package/dist/routines/routine-store.d.ts.map +1 -0
  84. package/dist/routines/routine-store.js +162 -0
  85. package/dist/routines/routine-store.js.map +1 -0
  86. package/dist/routines/schedule.d.ts +72 -0
  87. package/dist/routines/schedule.d.ts.map +1 -0
  88. package/dist/routines/schedule.js +263 -0
  89. package/dist/routines/schedule.js.map +1 -0
  90. package/dist/routines/scheduler.d.ts +34 -0
  91. package/dist/routines/scheduler.d.ts.map +1 -0
  92. package/dist/routines/scheduler.js +330 -0
  93. package/dist/routines/scheduler.js.map +1 -0
  94. package/dist/routines/types.d.ts +131 -0
  95. package/dist/routines/types.d.ts.map +1 -0
  96. package/dist/routines/types.js +9 -0
  97. package/dist/routines/types.js.map +1 -0
  98. package/dist/run/core/failure-taxonomy.d.ts +27 -0
  99. package/dist/run/core/failure-taxonomy.d.ts.map +1 -0
  100. package/dist/run/core/failure-taxonomy.js +16 -0
  101. package/dist/run/core/failure-taxonomy.js.map +1 -0
  102. package/dist/run/core/index.d.ts +5 -0
  103. package/dist/run/core/index.d.ts.map +1 -0
  104. package/dist/run/core/index.js +5 -0
  105. package/dist/run/core/index.js.map +1 -0
  106. package/dist/run/core/result.d.ts +25 -0
  107. package/dist/run/core/result.d.ts.map +1 -0
  108. package/dist/run/core/result.js +52 -0
  109. package/dist/run/core/result.js.map +1 -0
  110. package/dist/run/core/retry.d.ts +106 -0
  111. package/dist/run/core/retry.d.ts.map +1 -0
  112. package/dist/run/core/retry.js +210 -0
  113. package/dist/run/core/retry.js.map +1 -0
  114. package/dist/run/diagnostics/diagnostics.d.ts +86 -0
  115. package/dist/run/diagnostics/diagnostics.d.ts.map +1 -0
  116. package/dist/run/diagnostics/diagnostics.js +211 -0
  117. package/dist/run/diagnostics/diagnostics.js.map +1 -0
  118. package/dist/run/diagnostics/index.d.ts +3 -0
  119. package/dist/run/diagnostics/index.d.ts.map +1 -0
  120. package/dist/run/diagnostics/index.js +3 -0
  121. package/dist/run/diagnostics/index.js.map +1 -0
  122. package/dist/run/index.d.ts +16 -0
  123. package/dist/run/index.d.ts.map +1 -0
  124. package/dist/run/index.js +16 -0
  125. package/dist/run/index.js.map +1 -0
  126. package/dist/run-lifecycle.d.ts +118 -0
  127. package/dist/run-lifecycle.d.ts.map +1 -0
  128. package/dist/run-lifecycle.js +451 -0
  129. package/dist/run-lifecycle.js.map +1 -0
  130. package/dist/terminal-session.d.ts +139 -0
  131. package/dist/terminal-session.d.ts.map +1 -0
  132. package/dist/terminal-session.js +316 -0
  133. package/dist/terminal-session.js.map +1 -0
  134. package/dist/tokens.d.ts +9 -0
  135. package/dist/tokens.d.ts.map +1 -0
  136. package/dist/tokens.js +19 -0
  137. package/dist/tokens.js.map +1 -0
  138. package/dist/tool-executor.d.ts +97 -0
  139. package/dist/tool-executor.d.ts.map +1 -0
  140. package/dist/tool-executor.js +198 -0
  141. package/dist/tool-executor.js.map +1 -0
  142. 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":"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"}
@@ -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,5 @@
1
+ /** @module run/core — Run result types, error codes, failure taxonomy, and retry backoff policy. */
2
+ export * from './result.js';
3
+ export * from './failure-taxonomy.js';
4
+ export * from './retry.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -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,5 @@
1
+ /** @module run/core — Run result types, error codes, failure taxonomy, and retry backoff policy. */
2
+ export * from './result.js';
3
+ export * from './failure-taxonomy.js';
4
+ export * from './retry.js';
5
+ //# sourceMappingURL=index.js.map
@@ -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