@toddzheng024/dscode-bundle 0.3.0 → 0.5.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 (113) hide show
  1. package/THIRD_PARTY_NOTICES.md +15 -0
  2. package/cordis.patch.yml +5 -3
  3. package/package.json +14 -1
  4. package/plugins/clipboard-image/clipboard-image.swift +56 -0
  5. package/plugins/clipboard-image/index.mjs +70 -0
  6. package/plugins/code-review/git.mjs +95 -0
  7. package/plugins/code-review/index.mjs +88 -0
  8. package/plugins/dscode/index.mjs +17 -3
  9. package/plugins/email/cli.mjs +32 -0
  10. package/plugins/email/contacts.mjs +36 -0
  11. package/plugins/email/gmail-oauth.mjs +69 -0
  12. package/plugins/email/gmail-store.mjs +2 -0
  13. package/plugins/email/gmail.mjs +194 -0
  14. package/plugins/email/imap.mjs +136 -0
  15. package/plugins/email/inbox.d.mts +25 -0
  16. package/plugins/email/inbox.mjs +76 -0
  17. package/plugins/email/smtp.mjs +69 -0
  18. package/plugins/email/store.mjs +32 -0
  19. package/plugins/email-tools/index.mjs +44 -0
  20. package/plugins/session-metrics/index.mjs +6 -2
  21. package/plugins/session-metrics/rate.mjs +62 -0
  22. package/plugins/session-metrics/view.mjs +15 -9
  23. package/plugins/tui-tools/doctor-cli.mjs +22 -0
  24. package/plugins/tui-tools/doctor.mjs +141 -0
  25. package/plugins/tui-tools/index.mjs +14 -6
  26. package/plugins/ultra/policy.mjs +12 -1
  27. package/plugins/worktree-subagent/worktree.mjs +50 -0
  28. package/presets/dscode/agent.cordis.yml +7 -2
  29. package/vendor/deepseek/index.js +12 -2
  30. package/vendor/subagent/index.js +29 -9
  31. package/vendor/subagent-core/LICENSE +21 -0
  32. package/vendor/subagent-core/index.js +3232 -0
  33. package/vendor/subagent-core/invariant.js +76 -0
  34. package/vendor/subagent-core/typert.host.d.ts +3 -0
  35. package/vendor/subagent-core/typert.host.js +964 -0
  36. package/vendor/subagent-core/typert.remote-client.d.ts +27 -0
  37. package/vendor/subagent-core/typert.remote-client.js +160 -0
  38. package/vendor/subagent-core/types/assistant-output.d.ts +48 -0
  39. package/vendor/subagent-core/types/assistant-output.js +73 -0
  40. package/vendor/subagent-core/types/catalog.d.ts +77 -0
  41. package/vendor/subagent-core/types/catalog.js +103 -0
  42. package/vendor/subagent-core/types/child-agent.d.ts +150 -0
  43. package/vendor/subagent-core/types/child-agent.js +207 -0
  44. package/vendor/subagent-core/types/client.d.ts +8 -0
  45. package/vendor/subagent-core/types/client.js +7 -0
  46. package/vendor/subagent-core/types/continuation-activation.d.ts +251 -0
  47. package/vendor/subagent-core/types/continuation-activation.js +663 -0
  48. package/vendor/subagent-core/types/continuation-messages.d.ts +62 -0
  49. package/vendor/subagent-core/types/continuation-messages.js +102 -0
  50. package/vendor/subagent-core/types/continuation.d.ts +124 -0
  51. package/vendor/subagent-core/types/continuation.js +455 -0
  52. package/vendor/subagent-core/types/control-types.d.ts +146 -0
  53. package/vendor/subagent-core/types/control-types.js +9 -0
  54. package/vendor/subagent-core/types/control.d.ts +71 -0
  55. package/vendor/subagent-core/types/control.js +116 -0
  56. package/vendor/subagent-core/types/depth.d.ts +31 -0
  57. package/vendor/subagent-core/types/depth.js +39 -0
  58. package/vendor/subagent-core/types/descriptor.d.ts +144 -0
  59. package/vendor/subagent-core/types/descriptor.js +193 -0
  60. package/vendor/subagent-core/types/error.d.ts +11 -0
  61. package/vendor/subagent-core/types/error.js +14 -0
  62. package/vendor/subagent-core/types/inbox.d.ts +43 -0
  63. package/vendor/subagent-core/types/inbox.js +61 -0
  64. package/vendor/subagent-core/types/index.d.ts +316 -0
  65. package/vendor/subagent-core/types/index.js +511 -0
  66. package/vendor/subagent-core/types/internal.d.ts +58 -0
  67. package/vendor/subagent-core/types/internal.js +58 -0
  68. package/vendor/subagent-core/types/invariant.d.ts +13 -0
  69. package/vendor/subagent-core/types/invariant.js +91 -0
  70. package/vendor/subagent-core/types/lifecycle.d.ts +113 -0
  71. package/vendor/subagent-core/types/lifecycle.js +178 -0
  72. package/vendor/subagent-core/types/list-children.d.ts +65 -0
  73. package/vendor/subagent-core/types/list-children.js +381 -0
  74. package/vendor/subagent-core/types/out-of-process.d.ts +118 -0
  75. package/vendor/subagent-core/types/out-of-process.js +219 -0
  76. package/vendor/subagent-core/types/projection-types.d.ts +74 -0
  77. package/vendor/subagent-core/types/projection-types.js +7 -0
  78. package/vendor/subagent-core/types/projection.d.ts +95 -0
  79. package/vendor/subagent-core/types/projection.js +151 -0
  80. package/vendor/subagent-core/types/run-settlement.d.ts +17 -0
  81. package/vendor/subagent-core/types/run-settlement.js +70 -0
  82. package/vendor/subagent-core/types/types.d.ts +377 -0
  83. package/vendor/subagent-core/types/types.js +19 -0
  84. package/vendor/subagent-driver/LICENSE +21 -0
  85. package/vendor/subagent-driver/index.js +255 -0
  86. package/vendor/subagent-driver/types/index.d.ts +33 -0
  87. package/vendor/subagent-driver/types/structured.d.ts +42 -0
  88. package/vendor/subagent-fork/LICENSE +21 -0
  89. package/vendor/subagent-fork/index.js +61 -0
  90. package/vendor/subagent-fork/types/index.d.ts +20 -0
  91. package/vendor/subagent-spawn/LICENSE +21 -0
  92. package/vendor/subagent-spawn/index.js +45 -0
  93. package/vendor/subagent-spawn/types/index.d.ts +19 -0
  94. package/vendor/terminal/LICENSE +21 -0
  95. package/vendor/terminal/index.js +1013 -0
  96. package/vendor/terminal/types/config.d.ts +71 -0
  97. package/vendor/terminal/types/index.d.ts +36 -0
  98. package/vendor/terminal/types/sanitize.d.ts +47 -0
  99. package/vendor/terminal/types/session.d.ts +87 -0
  100. package/vendor/tui/dscode-clipboard-image/clipboard-image.swift +56 -0
  101. package/vendor/tui/dscode-clipboard-image/index.mjs +70 -0
  102. package/vendor/tui/dscode-email/cli.mjs +32 -0
  103. package/vendor/tui/dscode-email/contacts.mjs +36 -0
  104. package/vendor/tui/dscode-email/gmail-oauth.mjs +69 -0
  105. package/vendor/tui/dscode-email/gmail-store.mjs +2 -0
  106. package/vendor/tui/dscode-email/gmail.mjs +194 -0
  107. package/vendor/tui/dscode-email/imap.mjs +136 -0
  108. package/vendor/tui/dscode-email/inbox.d.mts +25 -0
  109. package/vendor/tui/dscode-email/inbox.mjs +76 -0
  110. package/vendor/tui/dscode-email/smtp.mjs +69 -0
  111. package/vendor/tui/dscode-email/store.mjs +32 -0
  112. package/vendor/tui/dscode-email.mjs +69 -0
  113. package/vendor/tui/index.mjs +473 -82
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Provider-side vocabulary for OUT-OF-PROCESS subagent backends — the pieces
3
+ * that enforce this seam's own contracts around a child in another process:
4
+ * the no-capabilities advertisement, timing-bound validation, child
5
+ * working-directory resolution (config override, else the delegating parent
6
+ * session's workspace), the never-reject result settlement, and the standard
7
+ * run-handle publication. Backends compose these with their own wire drivers;
8
+ * the process machinery itself (spawn, env scrub, managed-range teardown)
9
+ * belongs to the `dsh-subprocess` seam.
10
+ *
11
+ * @module @deepseek-ai/dsh-subagent/out-of-process
12
+ */
13
+ import { accessSync, constants, statSync } from 'node:fs';
14
+ import { isAbsolute, resolve } from 'node:path';
15
+ /** Maximum UTF-8 size of {@link SubagentResult.diagnostic}. */
16
+ const MAX_SUBAGENT_DIAGNOSTIC_BYTES = 4_096;
17
+ const DIAGNOSTIC_TRUNCATION_SUFFIX = '\n[diagnostic truncated]';
18
+ const utf8Encoder = new TextEncoder();
19
+ const utf8Decoder = new TextDecoder();
20
+ /**
21
+ * Limit provider-authored failure detail without splitting a UTF-8 sequence.
22
+ * @param diagnostic - safe diagnostic text produced by the provider.
23
+ * @returns the original text, or a visibly truncated value within the limit.
24
+ */
25
+ function limitSubagentDiagnostic(diagnostic) {
26
+ const bytes = utf8Encoder.encode(diagnostic);
27
+ if (bytes.byteLength <= MAX_SUBAGENT_DIAGNOSTIC_BYTES)
28
+ return diagnostic;
29
+ const suffixBytes = utf8Encoder.encode(DIAGNOSTIC_TRUNCATION_SUFFIX).byteLength;
30
+ let prefixBytes = MAX_SUBAGENT_DIAGNOSTIC_BYTES - suffixBytes;
31
+ while ((bytes[prefixBytes] & 0b1100_0000) === 0b1000_0000) {
32
+ prefixBytes -= 1;
33
+ }
34
+ return utf8Decoder.decode(bytes.subarray(0, prefixBytes))
35
+ + DIAGNOSTIC_TRUNCATION_SUFFIX;
36
+ }
37
+ /** Enforce the byte limit on a provider-returned diagnostic. */
38
+ function normalizeSubagentDiagnostic(result) {
39
+ return result.diagnostic === undefined
40
+ ? result
41
+ : { ...result, diagnostic: limitSubagentDiagnostic(result.diagnostic) };
42
+ }
43
+ /**
44
+ * The capability advertisement of an out-of-process backend: NONE. A child in
45
+ * another process cannot honor parent-enforced start features
46
+ * (`agentOptions`/`outputSchema`/`maxDepth`/`toolFilter`/`persona`), so the service rejects a
47
+ * request needing any of them before `start` runs — never accepted-then-ignored.
48
+ */
49
+ export const NO_START_CAPABILITIES = Object.freeze({
50
+ agentOptions: false,
51
+ outputSchema: false,
52
+ depthLimit: false,
53
+ toolFilter: false,
54
+ persona: false,
55
+ });
56
+ /**
57
+ * Assert a configured timing bound is a positive finite number (it bounds a
58
+ * teardown or shutdown wait; zero, negative, or NaN would skip or wedge it).
59
+ * @param prefix - the consuming plugin's diagnostic prefix (e.g. `subagent-acp`).
60
+ * @param name - the config field name, for the diagnostic.
61
+ * @param value - the configured value.
62
+ */
63
+ export function assertPositiveFinite(prefix, name, value) {
64
+ if (!Number.isFinite(value) || value <= 0) {
65
+ throw new Error(`${prefix}: ${name} must be a positive finite number`);
66
+ }
67
+ }
68
+ /**
69
+ * Whether `path` names an existing directory the harness can ENTER. The
70
+ * search-permission probe matters: `statSync().isDirectory()` is true for a
71
+ * mode-600 directory, but a subprocess cwd needs `X_OK` or spawn fails EACCES.
72
+ */
73
+ function isEnterableDirectory(path) {
74
+ try {
75
+ if (!statSync(path).isDirectory())
76
+ return false;
77
+ accessSync(path, constants.X_OK);
78
+ return true;
79
+ }
80
+ catch {
81
+ // statSync/accessSync throw only filesystem access errors here
82
+ // (ENOENT/EACCES/ENOTDIR/…), and every one of them means the path cannot
83
+ // serve as the child's cwd.
84
+ return false;
85
+ }
86
+ }
87
+ /**
88
+ * Assert `cwd` can actually host the child: absolute (it doubles as the
89
+ * child's workspace identity, and a relative path would be re-anchored to the
90
+ * server process's launch directory) and an existing directory (fail here,
91
+ * before the process boundary, instead of as an ambiguous spawn ENOENT).
92
+ * @param prefix - the consuming plugin's diagnostic prefix.
93
+ * @param label - which source supplied the value, for the diagnostic.
94
+ * @param cwd - the candidate working directory.
95
+ * @returns `cwd`, validated.
96
+ */
97
+ export function assertUsableCwd(prefix, label, cwd) {
98
+ if (!isAbsolute(cwd)) {
99
+ throw new Error(`${prefix}: ${label} must be an absolute path: ${cwd}`);
100
+ }
101
+ if (!isEnterableDirectory(cwd)) {
102
+ throw new Error(`${prefix}: ${label} is not an accessible directory: ${cwd}`);
103
+ }
104
+ return cwd;
105
+ }
106
+ /**
107
+ * Validate a configured `cwd` override ONCE, at plugin load: reject the empty
108
+ * string (`path.resolve('')` is the process cwd — it would silently
109
+ * reintroduce the launch-directory fallback this resolution removes),
110
+ * interpret a relative path against the harness launch directory, and require
111
+ * an enterable directory.
112
+ * @param prefix - the consuming plugin's diagnostic prefix.
113
+ * @param cwd - the configured override, or `undefined` when the config omits it.
114
+ * @returns the validated absolute override, or `undefined` when omitted.
115
+ */
116
+ export function validateConfiguredCwd(prefix, cwd) {
117
+ if (cwd === undefined)
118
+ return undefined;
119
+ if (cwd === '') {
120
+ throw new Error(`${prefix}: config cwd must not be empty — omit the key to inherit the parent session cwd`);
121
+ }
122
+ return assertUsableCwd(prefix, 'config cwd', resolve(cwd));
123
+ }
124
+ /**
125
+ * Resolve the child's working directory at start: the deployment override
126
+ * when configured (already validated at load), else the parent session's
127
+ * workspace cwd (validated here, its earliest resolvable point). Fails loud
128
+ * when neither exists — falling back to the harness process cwd would
129
+ * silently bind the child to the server's launch directory instead of the
130
+ * delegating session's workspace (one server process serves many sessions,
131
+ * each with its own cwd).
132
+ * @param prefix - the consuming plugin's diagnostic prefix.
133
+ * @param configured - the load-validated override, or `undefined`.
134
+ * @param parentCwd - the delegating parent session's workspace cwd, if any.
135
+ * @returns the absolute child working directory.
136
+ */
137
+ export function resolveChildCwd(prefix, configured, parentCwd) {
138
+ if (configured !== undefined)
139
+ return configured;
140
+ if (parentCwd === undefined) {
141
+ throw new Error(`${prefix}: no working directory for the child — configure \`cwd\` or delegate from a parent session that has one`);
142
+ }
143
+ return assertUsableCwd(prefix, 'parent session cwd', parentCwd);
144
+ }
145
+ /** Normalize an unknown thrown value to an Error (the catch binding is `unknown`). */
146
+ function toError(value) {
147
+ // The rejecting surfaces (wire clients, spawn failures) only throw
148
+ // `Error`s; the `String(value)` arm is a defensive fallback for a non-Error
149
+ // throw the typed surfaces cannot produce.
150
+ /* v8 ignore next */
151
+ return value instanceof Error ? value : new Error(String(value));
152
+ }
153
+ /**
154
+ * Settle an out-of-process run result under the seam contract: `result` never
155
+ * rejects after publication. A normally completed or rejected attempt resolves
156
+ * as `aborted` when cancellation already settled locally; another rejection is
157
+ * flattened to `stopReason: 'error'` through the contained diagnostic sink.
158
+ * Provider-returned diagnostics use the same byte limit. The abort listener is
159
+ * removed on every path.
160
+ * @param parts - the attempt, output snapshot, cancellation state, sink, and signal wiring.
161
+ * @returns the terminal result (never a rejection).
162
+ */
163
+ export async function settleRunResult(parts) {
164
+ try {
165
+ const result = await parts.attempt();
166
+ return parts.cancelled()
167
+ ? { output: parts.collectOutput(), stopReason: 'aborted' }
168
+ : normalizeSubagentDiagnostic(result);
169
+ }
170
+ catch (error) {
171
+ // Cover a rejection already queued when cancellation arrives.
172
+ if (parts.cancelled())
173
+ return { output: parts.collectOutput(), stopReason: 'aborted' };
174
+ // Flatten post-publication transport failures while preserving diagnostics.
175
+ try {
176
+ parts.onError?.(toError(error), 'error');
177
+ }
178
+ catch {
179
+ // The diagnostic sink cannot reject the run result.
180
+ }
181
+ const collected = parts.collectDiagnostic?.();
182
+ const diagnostic = collected === undefined
183
+ ? undefined
184
+ : limitSubagentDiagnostic(collected);
185
+ return {
186
+ output: parts.collectOutput(),
187
+ ...diagnostic === undefined ? {} : { diagnostic },
188
+ stopReason: 'error',
189
+ };
190
+ }
191
+ finally {
192
+ parts.signal.removeEventListener('abort', parts.onAbort);
193
+ }
194
+ }
195
+ /**
196
+ * Publish the seam run handle for an out-of-process child. `dispose()` is
197
+ * idempotent (one memoized teardown): it removes the abort listener, settles
198
+ * local cancellation — there is no assumption the child cooperates — and then
199
+ * awaits the backend's teardown to actual exit.
200
+ * @param parts - the run identity, result, cancellation wiring, and teardown.
201
+ * @returns the seam run handle (`localAgent` is `undefined` for remote runs).
202
+ */
203
+ export function subprocessRunHandle(parts) {
204
+ let disposal;
205
+ return {
206
+ id: parts.id,
207
+ localAgent: undefined,
208
+ result: parts.result,
209
+ dispose() {
210
+ if (disposal !== undefined)
211
+ return disposal;
212
+ parts.signal.removeEventListener('abort', parts.onAbort);
213
+ parts.requestCancel();
214
+ disposal = parts.teardown();
215
+ return disposal;
216
+ },
217
+ };
218
+ }
219
+ //# sourceMappingURL=out-of-process.js.map
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Pure client-safe subagent projection vocabulary.
3
+ *
4
+ * @module @deepseek-ai/dsh-subagent/projection-types
5
+ */
6
+ import type { SessionId, SessionSeq } from '@deepseek-ai/dsh-session/types';
7
+ /** One current direct-child discovery row materialized from parent facts. */
8
+ export type SubagentCatalogEntry = {
9
+ readonly id: SessionId;
10
+ readonly createdAt: number;
11
+ } & ({
12
+ readonly mode: 'one-shot';
13
+ readonly label?: string;
14
+ } | {
15
+ readonly mode: 'continuable';
16
+ readonly label: string;
17
+ });
18
+ /** Durable active-turn timing for one descriptor-backed child session. */
19
+ export interface SubagentTimingProjection {
20
+ /** Milliseconds accumulated across completed turns after the child's own descriptor. */
21
+ settledMs: number;
22
+ /** Same-cut bounds of the currently open turn, when one has not reached `turn/end`. */
23
+ active?: {
24
+ /** Start of the open turn. */
25
+ since: number;
26
+ /** Latest event time folded into this projection cut. */
27
+ through: number;
28
+ };
29
+ }
30
+ /**
31
+ * Durable identity of one descriptor-backed subagent session: lifecycle mode
32
+ * plus creation label, folded last-wins from `subagent/descriptor` events.
33
+ * Label strength follows the descriptor schema: a continuable child always
34
+ * carries one, a one-shot child may omit it.
35
+ */
36
+ export type SubagentIdentityProjection = {
37
+ /** A terminal one-shot child. */
38
+ mode: 'one-shot';
39
+ /** Optional durable creation label from the child's descriptor. */
40
+ label?: string;
41
+ /**
42
+ * Seq of the `subagent/descriptor` event this identity was folded from.
43
+ * `session.isOwnSeq(seq)` proves the identity comes from the child's
44
+ * OWN log suffix — where a descriptor is immutable once appended — and
45
+ * not from a fork seed's replayed ancestor descriptor.
46
+ */
47
+ seq: SessionSeq;
48
+ } | {
49
+ /** A resumable conversation. */
50
+ mode: 'continuable';
51
+ /** Durable creation label from the child's descriptor. */
52
+ label: string;
53
+ /** Seq of the folded descriptor event; see the one-shot arm for the own-suffix proof. */
54
+ seq: SessionSeq;
55
+ };
56
+ declare module '@deepseek-ai/dsh-session-projection/types' {
57
+ interface SessionProjectionMap {
58
+ /** Direct children in parent catalog event order, excluding fork-inherited facts. */
59
+ subagentCatalog: SubagentCatalogEntry[];
60
+ /** Active-turn duration for a descriptor-backed subagent session. */
61
+ subagentTiming: SubagentTimingProjection;
62
+ /**
63
+ * Identity of a descriptor-backed subagent session. `null` ⟺ no valid
64
+ * descriptor (missing, malformed, or unrecognized-version — deliberately
65
+ * undistinguished). The sentinel is deliberately serializable: a
66
+ * value pushed over JSON transports must survive `JSON.stringify`
67
+ * losslessly, where an `undefined` field would be dropped and a stale
68
+ * identity would survive on the receiving side. The entry itself stays
69
+ * non-optional.
70
+ */
71
+ subagent: SubagentIdentityProjection | null;
72
+ }
73
+ }
74
+ //# sourceMappingURL=projection-types.d.ts.map
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Pure client-safe subagent projection vocabulary.
3
+ *
4
+ * @module @deepseek-ai/dsh-subagent/projection-types
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=projection-types.js.map
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Pure session projections for subagent identity (mode/label) and active-turn
3
+ * duration.
4
+ *
5
+ * @module @deepseek-ai/dsh-subagent/projection
6
+ */
7
+ import { z } from 'zod';
8
+ import type { SessionEvent } from '@deepseek-ai/dsh-session';
9
+ import type { SubagentIdentityProjection, SubagentTimingProjection } from './projection-types.ts';
10
+ /** Fold state for a subagent's latest timing snapshot. */
11
+ export interface TimingState {
12
+ /** Milliseconds accumulated across completed post-descriptor turns. */
13
+ settledMs: number;
14
+ /** Current open interval kept paired inside the fold. */
15
+ active?: {
16
+ since: number;
17
+ through: number;
18
+ } | undefined;
19
+ /** Latest pre-descriptor turn start, promoted when the child's own descriptor arrives. */
20
+ pendingTurnStart?: number | undefined;
21
+ /** Whether the fold has crossed a descriptor in this logical log. */
22
+ descriptorSeen: boolean;
23
+ }
24
+ declare module '@deepseek-ai/dsh-session-projection/types' {
25
+ interface SessionProjectionStateMap {
26
+ subagentTiming: TimingState;
27
+ subagent: IdentityState;
28
+ }
29
+ }
30
+ /**
31
+ * Fold turn boundaries around the child's own durable descriptor.
32
+ *
33
+ * A fork seed may contain an ancestor descriptor and completed turns. Every
34
+ * descriptor therefore resets the accumulated state; the healthy catalog
35
+ * admits only a child with exactly one descriptor in its own suffix, making
36
+ * the final reset the child's authoritative timing origin.
37
+ */
38
+ export declare const subagentTimingProjectionDefinition: {
39
+ key: "subagentTiming";
40
+ stateSchema: z.ZodType<TimingState, unknown, z.core.$ZodTypeInternals<TimingState, unknown>>;
41
+ init: () => {
42
+ descriptorSeen: false;
43
+ settledMs: number;
44
+ };
45
+ apply: (state: NoInfer<TimingState>, event: SessionEvent) => {
46
+ /** Milliseconds accumulated across completed post-descriptor turns. */
47
+ settledMs: number;
48
+ /** Current open interval kept paired inside the fold. */
49
+ active?: {
50
+ since: number;
51
+ through: number;
52
+ } | undefined;
53
+ /** Whether the fold has crossed a descriptor in this logical log. */
54
+ descriptorSeen: boolean;
55
+ };
56
+ wire: {
57
+ viewSchema: z.ZodType<SubagentTimingProjection, unknown, z.core.$ZodTypeInternals<SubagentTimingProjection, unknown>>;
58
+ view: (state: NoInfer<TimingState>) => {
59
+ active?: {
60
+ since: number;
61
+ through: number;
62
+ };
63
+ settledMs: number;
64
+ };
65
+ };
66
+ stateVersion: number;
67
+ };
68
+ interface IdentityState {
69
+ /** Identity from the last valid descriptor; absent before one, and after an invalid one. */
70
+ identity?: SubagentIdentityProjection | undefined;
71
+ }
72
+ /**
73
+ * Fold the durable mode/label identity from `subagent/descriptor` events,
74
+ * last-wins: a fork seed may replay an ancestor's descriptor, and the child's
75
+ * own descriptor must override it — the same reset discipline as
76
+ * {@link subagentTimingProjectionDefinition}. A malformed or unknown-version
77
+ * payload resets to the `null` sentinel instead of throwing, so a fork of a
78
+ * healthy ancestor never inherits an identity its own descriptor failed to
79
+ * establish — and the reset survives every JSON push frame, so a consumer
80
+ * holding the earlier identity replaces it instead of keeping it stale;
81
+ * `null` ⟺ no valid descriptor, with the causes deliberately undistinguished.
82
+ */
83
+ export declare const subagentIdentityProjectionDefinition: {
84
+ key: "subagent";
85
+ stateSchema: z.ZodType<IdentityState, unknown, z.core.$ZodTypeInternals<IdentityState, unknown>>;
86
+ init: () => {};
87
+ apply: (state: NoInfer<IdentityState>, event: SessionEvent) => IdentityState;
88
+ wire: {
89
+ viewSchema: z.ZodNullable<z.ZodType<SubagentIdentityProjection, unknown, z.core.$ZodTypeInternals<SubagentIdentityProjection, unknown>>>;
90
+ view: (state: NoInfer<IdentityState>) => SubagentIdentityProjection | null;
91
+ };
92
+ stateVersion: number;
93
+ };
94
+ export {};
95
+ //# sourceMappingURL=projection.d.ts.map
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Pure session projections for subagent identity (mode/label) and active-turn
3
+ * duration.
4
+ *
5
+ * @module @deepseek-ai/dsh-subagent/projection
6
+ */
7
+ import { z } from 'zod';
8
+ import { SessionSeq } from '@deepseek-ai/dsh-session';
9
+ import { foldSubagentDescriptor } from "./descriptor.js";
10
+ const activeIntervalSchema = z.object({
11
+ since: z.number().int().nonnegative(),
12
+ through: z.number().int().nonnegative(),
13
+ }).strict();
14
+ const projectionSchema = z.object({
15
+ settledMs: z.number().int().nonnegative(),
16
+ active: activeIntervalSchema.optional(),
17
+ }).strict().transform(({ settledMs, active }) => ({
18
+ settledMs,
19
+ ...active === undefined ? {} : { active },
20
+ }));
21
+ const timingStateSchema = z.object({
22
+ settledMs: z.number().int().nonnegative(),
23
+ active: activeIntervalSchema.optional(),
24
+ pendingTurnStart: z.number().int().nonnegative().optional(),
25
+ descriptorSeen: z.boolean(),
26
+ }).strict();
27
+ /**
28
+ * Fold turn boundaries around the child's own durable descriptor.
29
+ *
30
+ * A fork seed may contain an ancestor descriptor and completed turns. Every
31
+ * descriptor therefore resets the accumulated state; the healthy catalog
32
+ * admits only a child with exactly one descriptor in its own suffix, making
33
+ * the final reset the child's authoritative timing origin.
34
+ */
35
+ export const subagentTimingProjectionDefinition = {
36
+ key: 'subagentTiming',
37
+ stateSchema: timingStateSchema,
38
+ init: () => ({ descriptorSeen: false, settledMs: 0 }),
39
+ apply: (state, event) => {
40
+ if (event.type === 'turn/start') {
41
+ return state.descriptorSeen
42
+ ? { ...state, active: { since: event.time, through: event.time } }
43
+ : { ...state, pendingTurnStart: event.time };
44
+ }
45
+ if (event.type === 'subagent/descriptor') {
46
+ const activeSince = state.active?.since ?? state.pendingTurnStart;
47
+ return {
48
+ descriptorSeen: true,
49
+ settledMs: 0,
50
+ ...(activeSince === undefined
51
+ ? {}
52
+ : { active: { since: activeSince, through: event.time } }),
53
+ };
54
+ }
55
+ if (event.type === 'turn/end') {
56
+ if (!state.descriptorSeen) {
57
+ if (state.pendingTurnStart === undefined)
58
+ return state;
59
+ const { pendingTurnStart: _closed, ...next } = state;
60
+ return next;
61
+ }
62
+ if (state.active === undefined)
63
+ return state;
64
+ const { active, ...rest } = state;
65
+ return {
66
+ ...rest,
67
+ settledMs: state.settledMs + Math.max(0, event.time - active.since),
68
+ };
69
+ }
70
+ if (state.active === undefined)
71
+ return state;
72
+ return { ...state, active: { ...state.active, through: event.time } };
73
+ },
74
+ wire: {
75
+ viewSchema: projectionSchema,
76
+ view: state => ({
77
+ settledMs: state.settledMs,
78
+ ...(state.active === undefined ? {} : { active: state.active }),
79
+ }),
80
+ },
81
+ stateVersion: 2,
82
+ };
83
+ // The cast bridges only the optional-label arm: Zod's optional output
84
+ // includes explicit `undefined`, which exactOptionalPropertyTypes excludes
85
+ // from the public interface. The no-value state itself is the serializable
86
+ // `null` arm — never `undefined` — so every registry read and push frame
87
+ // survives JSON.stringify losslessly.
88
+ const identityValueSchema = z.discriminatedUnion('mode', [
89
+ z.object({
90
+ mode: z.literal('one-shot'),
91
+ label: z.string().optional(),
92
+ seq: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).transform(SessionSeq),
93
+ }).strict(),
94
+ z.object({
95
+ mode: z.literal('continuable'),
96
+ label: z.string(),
97
+ seq: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).transform(SessionSeq),
98
+ }).strict(),
99
+ ]);
100
+ const identitySchema = identityValueSchema.nullable();
101
+ const identityStateSchema = z.object({
102
+ identity: identityValueSchema.optional(),
103
+ }).strict();
104
+ /** Interpret one `subagent/descriptor` event's identity; no value when the payload cannot be trusted. */
105
+ function descriptorIdentity(event) {
106
+ let descriptor;
107
+ try {
108
+ descriptor = foldSubagentDescriptor([event]);
109
+ }
110
+ catch {
111
+ // Only a malformed current-version payload throws in descriptor parsing;
112
+ // a projection fold must never throw, so damage folds to no value.
113
+ descriptor = undefined;
114
+ }
115
+ if (descriptor === undefined)
116
+ return undefined;
117
+ return descriptor.mode === 'one-shot'
118
+ ? {
119
+ mode: 'one-shot',
120
+ ...descriptor.label !== undefined ? { label: descriptor.label } : {},
121
+ seq: event.seq,
122
+ }
123
+ : { mode: 'continuable', label: descriptor.label, seq: event.seq };
124
+ }
125
+ /**
126
+ * Fold the durable mode/label identity from `subagent/descriptor` events,
127
+ * last-wins: a fork seed may replay an ancestor's descriptor, and the child's
128
+ * own descriptor must override it — the same reset discipline as
129
+ * {@link subagentTimingProjectionDefinition}. A malformed or unknown-version
130
+ * payload resets to the `null` sentinel instead of throwing, so a fork of a
131
+ * healthy ancestor never inherits an identity its own descriptor failed to
132
+ * establish — and the reset survives every JSON push frame, so a consumer
133
+ * holding the earlier identity replaces it instead of keeping it stale;
134
+ * `null` ⟺ no valid descriptor, with the causes deliberately undistinguished.
135
+ */
136
+ export const subagentIdentityProjectionDefinition = {
137
+ key: 'subagent',
138
+ stateSchema: identityStateSchema,
139
+ init: () => ({}),
140
+ apply: (state, event) => {
141
+ if (event.type !== 'subagent/descriptor')
142
+ return state;
143
+ const identity = descriptorIdentity(event);
144
+ return identity === undefined ? {} : { identity };
145
+ },
146
+ wire: { viewSchema: identitySchema, view: state => state.identity ?? null },
147
+ // Bumped when the identity gained its `seq` field: an older checkpoint row
148
+ // would replay into a value the schema rejects, so it must refold instead.
149
+ stateVersion: 2,
150
+ };
151
+ //# sourceMappingURL=projection.js.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Settlement of one ONE-SHOT subagent run into a background-Task outcome. Only
3
+ * the one-shot background path uses Jobs; continuable children have no Task,
4
+ * no per-message result, and no Task cancellation.
5
+ *
6
+ * @module @deepseek-ai/dsh-subagent/run-settlement
7
+ */
8
+ import type { JobOutcome } from '@deepseek-ai/dsh-jobs';
9
+ import type { SubagentRun } from './types.ts';
10
+ /**
11
+ * Await the child result, dispose the run, then return its task outcome. Result
12
+ * and disposal failures become `failed`; when both fail, both details survive.
13
+ * @param run - live run to settle and release.
14
+ * @returns outcome after child resources are released.
15
+ */
16
+ export declare function settleRun(run: SubagentRun): Promise<JobOutcome>;
17
+ //# sourceMappingURL=run-settlement.d.ts.map
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Settlement of one ONE-SHOT subagent run into a background-Task outcome. Only
3
+ * the one-shot background path uses Jobs; continuable children have no Task,
4
+ * no per-message result, and no Task cancellation.
5
+ *
6
+ * @module @deepseek-ai/dsh-subagent/run-settlement
7
+ */
8
+ /** Flatten a child's final output blocks to the task's final text. */
9
+ function finalText(blocks) {
10
+ return blocks
11
+ .filter((block) => block.type === 'text')
12
+ .map(block => block.text)
13
+ .join('');
14
+ }
15
+ /** Render a failed stop reason with optional provider-authored detail. */
16
+ function failureDetail(result) {
17
+ const stopReason = result.stopReason;
18
+ return result.diagnostic === undefined
19
+ ? stopReason
20
+ : `${stopReason}; diagnostic: ${result.diagnostic}`;
21
+ }
22
+ /**
23
+ * Map a child result to the task outcome: completed carries final text, local
24
+ * cancellation (`aborted` without a diagnostic) is killed, and provider-
25
+ * diagnosed remote aborts plus every other reason are failed without partial
26
+ * output.
27
+ * @param result - child terminal result.
28
+ * @returns outcome for the `ctx.jobs` registration.
29
+ */
30
+ function runOutcome(result) {
31
+ switch (result.stopReason) {
32
+ case 'completed':
33
+ return { status: 'completed', output: finalText(result.output) };
34
+ case 'aborted':
35
+ return result.diagnostic === undefined
36
+ ? { status: 'killed' }
37
+ : { status: 'failed', detail: failureDetail(result) };
38
+ case 'error':
39
+ case 'max-tokens':
40
+ case 'refusal':
41
+ return { status: 'failed', detail: failureDetail(result) };
42
+ // Merge-extensible reasons remain failures with provider-authored detail.
43
+ default:
44
+ return { status: 'failed', detail: failureDetail(result) };
45
+ }
46
+ }
47
+ /**
48
+ * Await the child result, dispose the run, then return its task outcome. Result
49
+ * and disposal failures become `failed`; when both fail, both details survive.
50
+ * @param run - live run to settle and release.
51
+ * @returns outcome after child resources are released.
52
+ */
53
+ export async function settleRun(run) {
54
+ let outcome;
55
+ try {
56
+ outcome = runOutcome(await run.result);
57
+ }
58
+ catch (error) {
59
+ outcome = { status: 'failed', detail: String(error) };
60
+ }
61
+ try {
62
+ await run.dispose();
63
+ }
64
+ catch (error) {
65
+ const prefix = outcome.detail === undefined ? '' : `${outcome.detail}; `;
66
+ return { status: 'failed', detail: `${prefix}dispose failed: ${String(error)}` };
67
+ }
68
+ return outcome;
69
+ }
70
+ //# sourceMappingURL=run-settlement.js.map