dsh-ptc-plus 0.3.2 → 0.4.2

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 (206) hide show
  1. package/README.en.md +63 -80
  2. package/README.md +63 -80
  3. package/assets/ptc-plus-bindings-en.png +0 -0
  4. package/assets/ptc-plus-bindings-zh.png +0 -0
  5. package/assets/ptc-plus-repair-en.png +0 -0
  6. package/assets/ptc-plus-repair-zh.png +0 -0
  7. package/assets/ptc-plus-repl-workspace-en.png +0 -0
  8. package/assets/ptc-plus-repl-workspace-zh.png +0 -0
  9. package/assets/ptc-plus-settings-en.png +0 -0
  10. package/assets/ptc-plus-settings-zh.png +0 -0
  11. package/client.js +30827 -742
  12. package/compiler-amaro.cjs +5 -0
  13. package/compiler-amaro.cjs.map +6 -0
  14. package/compiler-asset-124f80c1bba92ff5fe79cfec6e8ad518f476de4a42514cae3919538887f510e8.txt +1 -0
  15. package/compiler-assets.json +3 -0
  16. package/compiler-core.cjs +556 -0
  17. package/compiler-core.cjs.map +6 -0
  18. package/compiler-platform.cjs +2 -0
  19. package/compiler-platform.cjs.map +6 -0
  20. package/compiler-typescript.cjs +388 -0
  21. package/compiler-typescript.cjs.map +6 -0
  22. package/docs/README.md +7 -0
  23. package/docs/adr/0004-declare-program-bindings-at-the-host.md +1 -1
  24. package/docs/adr/0006-rewrite-module-syntax-with-an-ast.md +5 -3
  25. package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +1 -1
  26. package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -0
  27. package/docs/adr/0010-session-log-derived-recovery-tips.md +59 -5
  28. package/docs/adr/0011-isolate-the-local-recovery-tip-provider.md +1 -1
  29. package/docs/adr/0012-preserve-mixed-destructuring-initialization.md +3 -1
  30. package/docs/adr/0013-isolate-repl-redeclaration-convenience.md +3 -1
  31. package/docs/adr/0014-persist-cell-rewrite-policy.md +6 -2
  32. package/docs/adr/0015-preserve-source-positions-through-rewrites.md +1 -1
  33. package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
  34. package/docs/adr/0017-track-the-latest-dsh-public-surface.md +49 -0
  35. package/docs/adr/0018-use-versioned-fixture-for-ab-benchmark.md +3 -3
  36. package/docs/adr/0019-plugin-settings-and-kill-switch.md +5 -3
  37. package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
  38. package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +12 -0
  39. package/docs/adr/0023-global-user-bindings.md +104 -0
  40. package/docs/adr/0024-repl-console-observation.md +33 -0
  41. package/docs/adr/0025-use-versioned-logical-binding-identities.md +119 -0
  42. package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +21 -0
  43. package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +37 -0
  44. package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +103 -0
  45. package/docs/adr/0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md +27 -0
  46. package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +27 -0
  47. package/docs/architecture.md +77 -21
  48. package/docs/binding-review-placement-design.md +200 -0
  49. package/docs/capability-projection.md +1 -1
  50. package/docs/client-ui.md +95 -19
  51. package/docs/durability-design.md +28 -10
  52. package/docs/evaluation.md +78 -6
  53. package/docs/installation.md +41 -6
  54. package/docs/publishing.md +5 -3
  55. package/docs/runtime-reference.md +76 -25
  56. package/docs/semantic-validation.md +29 -0
  57. package/docs/stateful-binding-updates-proposal.md +759 -0
  58. package/docs/user-bindings.en.md +66 -0
  59. package/docs/user-bindings.md +66 -0
  60. package/docs/value-wire.md +3 -3
  61. package/docs/verification-optimization-work.md +41 -0
  62. package/docs/verification.md +71 -0
  63. package/index.js +369 -92
  64. package/internal/ast-traversal.js +5 -2
  65. package/internal/async-local-storage-intrinsics.js +38 -0
  66. package/internal/binding-declaration-contract.js +8 -0
  67. package/internal/binding-descriptors.js +1 -0
  68. package/internal/binding-pattern-traversal.js +25 -0
  69. package/internal/binding-pattern.js +26 -29
  70. package/internal/binding-source-analysis.js +708 -0
  71. package/internal/binding-update-policy.js +72 -0
  72. package/internal/bounded-worker-round.js +56 -0
  73. package/internal/callable-definition-environment.js +42 -0
  74. package/internal/callable-source-catalog.js +129 -0
  75. package/internal/callable-source-encoding.js +47 -0
  76. package/internal/callable-source-facts.js +459 -0
  77. package/internal/callable-source-format.js +2 -0
  78. package/internal/callable-source-lowering.js +123 -0
  79. package/internal/cell-analysis-contract.js +25 -0
  80. package/internal/cell-analysis.js +253 -290
  81. package/internal/cell-completion.js +15 -0
  82. package/internal/cell-error.js +7 -0
  83. package/internal/cell-parser.js +66 -0
  84. package/internal/cell-rewriter.js +3 -677
  85. package/internal/commonjs-export-evidence.js +63 -0
  86. package/internal/commonjs-source-evidence.js +72 -0
  87. package/internal/compiler-amaro.cjs +2 -0
  88. package/internal/compiler-compression.js +12 -0
  89. package/internal/compiler-data.js +58 -0
  90. package/internal/compiler-descriptors.js +54 -0
  91. package/internal/compiler-dynamic-cache.js +48 -0
  92. package/internal/compiler-entry.js +35 -0
  93. package/internal/compiler-generated-names.js +17 -0
  94. package/internal/compiler-intrinsics.js +50 -0
  95. package/internal/compiler-module-links.js +37 -0
  96. package/internal/compiler-operations.js +61 -0
  97. package/internal/compiler-platform-factory.js +84 -0
  98. package/internal/compiler-platform.js +136 -0
  99. package/internal/compiler-record-roles.js +53 -0
  100. package/internal/compiler-region-output.js +259 -0
  101. package/internal/compiler-resource-lowering.js +103 -0
  102. package/internal/compiler-scope-facts.js +115 -0
  103. package/internal/compiler-service.js +109 -0
  104. package/internal/compiler-source-cache.js +32 -0
  105. package/internal/compiler-source-pieces.js +93 -0
  106. package/internal/compiler-source-regions.js +132 -0
  107. package/internal/compiler-storage-source.js +54 -0
  108. package/internal/compiler-text.js +15 -0
  109. package/internal/compiler-typescript.cjs +2 -0
  110. package/internal/config-spec.js +83 -27
  111. package/internal/cordis-tools-owner.js +130 -21
  112. package/internal/diagnostic.js +27 -1
  113. package/internal/direct-surface-owner.js +76 -23
  114. package/internal/dynamic-binding-evidence.js +16 -0
  115. package/internal/dynamic-environment-compiler.js +779 -0
  116. package/internal/dynamic-environment-integration.js +91 -0
  117. package/internal/dynamic-environment-runtime.js +383 -0
  118. package/internal/dynamic-native-calls.js +153 -0
  119. package/internal/dynamic-native-runtime.js +262 -0
  120. package/internal/dynamic-scope-analysis.js +90 -0
  121. package/internal/edit-transport-owner.js +27 -2
  122. package/internal/execution-seam-compat.js +193 -0
  123. package/internal/failure-reporting.js +136 -28
  124. package/internal/host-rpc.js +66 -0
  125. package/internal/isolated-worker.js +555 -0
  126. package/internal/kernel-child.js +139 -0
  127. package/internal/kernel-worker.js +1179 -233
  128. package/internal/language-semantics.js +14 -0
  129. package/internal/legacy-binding-catalog.js +48 -0
  130. package/internal/legacy-cell-analysis.js +600 -0
  131. package/internal/legacy-cell-parser.js +50 -0
  132. package/internal/legacy-cell-rewriter.js +631 -0
  133. package/internal/legacy-repl-convenience.js +291 -0
  134. package/internal/managed-module-operations.js +170 -0
  135. package/internal/module-compilation.js +322 -0
  136. package/internal/module-runtime-platform.js +19 -0
  137. package/internal/module-transform-contract.js +42 -0
  138. package/internal/native-await-declarations.js +68 -0
  139. package/internal/native-root-compilation.js +54 -0
  140. package/internal/native-root-dynamic.js +67 -0
  141. package/internal/record-utils.js +24 -15
  142. package/internal/recovery-tips.js +27 -15
  143. package/internal/rejected-cell-editor.js +13 -8
  144. package/internal/repl-convenience.js +2 -289
  145. package/internal/repl-memory-projection.js +81 -12
  146. package/internal/repl-observation-interest.js +70 -0
  147. package/internal/repl-rewrite-contract.js +6 -0
  148. package/internal/repl-scope-normalizer.js +2091 -0
  149. package/internal/repl-value-observer.js +117 -0
  150. package/internal/rpc-contract.js +52 -0
  151. package/internal/runtime-bridge-owner.js +172 -28
  152. package/internal/runtime-config.js +48 -52
  153. package/internal/runtime-contexts.js +82 -10
  154. package/internal/runtime-intrinsics.js +101 -0
  155. package/internal/runtime-messages.js +111 -0
  156. package/internal/session-cell-executor.js +402 -87
  157. package/internal/session-journal-recovery.js +684 -0
  158. package/internal/session-journal-schema.js +123 -0
  159. package/internal/session-journal.js +199 -654
  160. package/internal/session-log-view.js +42 -9
  161. package/internal/session-runtime.js +257 -95
  162. package/internal/session-state.js +247 -71
  163. package/internal/source-position-map.js +230 -71
  164. package/internal/stateful-module-compiler.js +154 -0
  165. package/internal/stateful-module-runtime.js +303 -0
  166. package/internal/stateful-root-compiler.js +600 -0
  167. package/internal/stateful-root-runtime.js +340 -0
  168. package/internal/typert-codec-compat.js +28 -0
  169. package/internal/typescript-transform.js +31 -0
  170. package/internal/user-binding-authoring.js +70 -0
  171. package/internal/user-binding-console-worker.js +244 -0
  172. package/internal/user-binding-console.js +148 -0
  173. package/internal/user-binding-draft-projection.js +335 -0
  174. package/internal/user-binding-model-context.js +31 -0
  175. package/internal/user-binding-runner.js +56 -0
  176. package/internal/user-bindings-owner.js +1065 -0
  177. package/internal/user-bindings-store.js +293 -0
  178. package/internal/user-bindings.js +361 -0
  179. package/internal/value-wire-schema.js +28 -0
  180. package/internal/value-wire.js +130 -91
  181. package/internal/worker-client.js +57 -67
  182. package/internal/worker-cwd-virtualization.js +80 -53
  183. package/internal/worker-environment.js +65 -0
  184. package/internal/worker-output-relay.js +29 -0
  185. package/internal/worker-repl-realm.js +134 -0
  186. package/internal/worker-shutdown.js +14 -0
  187. package/package.json +76 -7
  188. package/src/client-activity.js +95 -53
  189. package/src/client-authoring-view.js +580 -0
  190. package/src/client-binding-review.js +230 -0
  191. package/src/client-bindings-data.js +29 -0
  192. package/src/client-catalog.js +127 -0
  193. package/src/client-code-editor.js +84 -0
  194. package/src/client-console.js +112 -0
  195. package/src/client-copy.js +370 -0
  196. package/src/client-feature-gates.js +62 -0
  197. package/src/client-host-compat.js +150 -0
  198. package/src/client-indicator-view.js +267 -0
  199. package/src/client-repl-view.js +292 -0
  200. package/src/client-rpc.js +20 -0
  201. package/src/client-settings-view.js +221 -0
  202. package/src/client-styles.js +106 -0
  203. package/src/client-tool-view.js +108 -0
  204. package/src/client-transport.js +159 -0
  205. package/src/client-workbench.js +521 -0
  206. package/src/client.js +233 -615
@@ -1,27 +1,108 @@
1
1
  import { diagnostic } from './diagnostic.js'
2
+ import { createHash } from 'node:crypto'
3
+ import { createPrivateAsyncLocalStorage } from './async-local-storage-intrinsics.js'
4
+ import { runtimeIntrinsics as internal } from './runtime-intrinsics.js'
2
5
 
6
+ const { Object, RegExp, isArray, numberIsSafeInteger, toNumber, toString,
7
+ splitString, startsWith, endsWith, sliceString, searchString, replaceString,
8
+ trimString, regexpExec, prependArray, join, bufferByteLength, bufferIsBuffer,
9
+ bufferSubarray, bufferToString } = internal
3
10
  export const MAX_ERROR_LOG_BYTES = 4 * 1024
4
11
  export const FAILURE_HINT_THRESHOLD = 3
5
12
  export const LONG_CELL_CODE_UNITS = 2_000
6
13
  const BINDING_FAILURE = Symbol('binding failure')
14
+ const PROGRAM_FAILURES = new internal.WeakMap()
15
+ const MAX_CAUSE_CODE_UNITS = 2048
16
+ const exceptionOrigins = createPrivateAsyncLocalStorage()
17
+ const runExceptionScope = exceptionOrigins.run.bind(exceptionOrigins)
18
+ const currentExceptionScope = exceptionOrigins.getStore.bind(exceptionOrigins)
7
19
 
8
- export function markBindingFailure(error) {
9
- Object.defineProperty(error, BINDING_FAILURE, { value: true })
20
+ /** Propagation facts belong to one execution, never to a user-thrown value. */
21
+ export function createExceptionOriginScope() {
22
+ const state = { origins: new internal.Map(), sourceFailures: new internal.Set(), open: true }
23
+ return {
24
+ run: callback => runExceptionScope(state, callback),
25
+ origins: error => internal.mapGet(state.origins, error),
26
+ sourceFailure: error => internal.setHas(state.sourceFailures, error),
27
+ close() { state.open = false; internal.mapClear(state.origins); internal.setClear(state.sourceFailures) },
28
+ }
29
+ }
30
+
31
+ export function recordExceptionOrigin(error, origin, { reset = false, sourceFailure = false } = {}) {
32
+ const state = currentExceptionScope()
33
+ if (state?.open !== true) return
34
+ if (reset) { internal.mapDelete(state.origins, error); internal.setDelete(state.sourceFailures, error) }
35
+ if (sourceFailure) internal.setAdd(state.sourceFailures, error)
36
+ if (typeof origin !== 'string') return
37
+ const origins = internal.mapGet(state.origins, error) ?? []
38
+ if (!internal.includes(origins, origin)) internal.appendArray(origins, origin)
39
+ internal.mapSet(state.origins, error, origins)
40
+ }
41
+
42
+ const SOURCE_LINE_BREAK = /\r\n|[\n\r\u2028\u2029]/
43
+ const sourceLines = source => splitString(source, SOURCE_LINE_BREAK)
44
+
45
+ /** A cell position is a 1-based line and column inside its source. The column
46
+ * may address the position just after the last character of a line. */
47
+ function boundedPosition(position, lines) {
48
+ if (position === null || typeof position !== 'object'
49
+ || !numberIsSafeInteger(position.line) || position.line < 1
50
+ || !numberIsSafeInteger(position.column) || position.column < 1
51
+ || position.line > lines.length || position.column > lines[position.line - 1].length + 1) return undefined
52
+ return { line: position.line, column: position.column }
53
+ }
54
+
55
+ /** Validate a candidate against the source that owns the reported cell. */
56
+ export function cellPosition(position, source) {
57
+ return boundedPosition(position, sourceLines(source))
58
+ }
59
+
60
+ /** Only a fact belonging to the current original source can bypass its map. */
61
+ export function exceptionOriginPosition(origins, source) {
62
+ if (!isArray(origins)) return undefined
63
+ const prefix = `eval:${createHash('sha256').update(source).digest('hex')}:`
64
+ const lines = sourceLines(source)
65
+ for (let index = 0; index < origins.length; index += 1) {
66
+ const origin = origins[index]
67
+ if (typeof origin !== 'string' || !startsWith(origin, prefix)) continue
68
+ const match = regexpExec(/^(\d+):(\d+)$/, sliceString(origin, prefix.length))
69
+ if (match === null) continue
70
+ const position = boundedPosition({ line: toNumber(match[1]), column: toNumber(match[2]) }, lines)
71
+ if (position !== undefined) return position
72
+ }
73
+ return undefined
74
+ }
75
+
76
+ export function markBindingFailure(error, kind = 'lexical') {
77
+ Object.defineProperty(error, BINDING_FAILURE, { value: kind })
10
78
  return error
11
79
  }
12
80
 
13
- function repeatedFailureDiagnostic(kind, streak) {
14
- if (kind === 'binding') {
81
+ /** Preserve worker-owned failure identity without trusting user exception text. */
82
+ export function programBindingError(kind, message) {
83
+ const error = new internal.Error(message)
84
+ internal.weakMapSet(PROGRAM_FAILURES, error, kind)
85
+ return error
86
+ }
87
+
88
+ function repeatedFailureDiagnostic(kind, streak, stateEffect) {
89
+ if (kind === 'lexical' || kind === 'capability') {
90
+ const capability = kind === 'capability'
15
91
  return diagnostic({
16
92
  code: 'PTC-W001',
17
93
  severity: 'warning',
18
94
  phase: 'execute',
19
- message: `this cell failed ${streak} times with the same binding error; inspect the live binding names and change the expression before retrying`,
20
- stateEffect: 'unchanged',
21
- help: [
95
+ message: `this cell failed ${streak} times with the same ${capability ? 'capability' : 'lexical binding'} error; resolve the reported name before continuing`,
96
+ stateEffect,
97
+ cause: { code: capability ? 'PTC-CAPABILITY' : 'PTC-LOCAL', message: capability ? 'current program capability lookup failed' : 'local name or declaration conflict' },
98
+ help: capability ? [
22
99
  'inspect live bindings with capabilities.tree(), capabilities.find(), or capabilities.inspect()',
23
100
  'call available typed members through tools.*',
24
101
  'do not repeat the same unresolved binding expression',
102
+ ] : [
103
+ 'inspect the visible cell source and results for the local declaration, scope, or initialization failure',
104
+ 'correct the name or initialization; use a fresh name or block for a declaration conflict',
105
+ 'capabilities describes program APIs, not session-local variables; discovery cannot prove local state',
25
106
  ],
26
107
  })
27
108
  }
@@ -30,7 +111,7 @@ function repeatedFailureDiagnostic(kind, streak) {
30
111
  severity: 'warning',
31
112
  phase: 'execute',
32
113
  message: `this cell failed ${streak} times with the same error; inspect the reported cause and change the approach before retrying`,
33
- stateEffect: 'unchanged',
114
+ stateEffect,
34
115
  help: ['inspect the reported cause', 'change the inputs or approach before retrying'],
35
116
  })
36
117
  }
@@ -48,28 +129,36 @@ export function messageOf(error, fallback = 'Unprintable error') {
48
129
  const message = safeProperty(error, 'message')
49
130
  if (typeof message === 'string') return message
50
131
  try {
51
- return String(error)
132
+ return toString(error)
52
133
  } catch {
53
134
  return fallback
54
135
  }
55
136
  }
56
137
 
138
+ /** Preserve the child-process exit facts in every helper failure diagnostic. */
139
+ export function processExitDescription(code, signal) {
140
+ return signal === null || signal === undefined
141
+ ? `code ${code}`
142
+ : `code ${code}, signal ${signal}`
143
+ }
144
+
57
145
  export function firstLine(value, fallback = undefined) {
58
146
  if (typeof value !== 'string') return fallback
59
- const line = value.split(/[\r\n]/, 1)[0]
147
+ const line = splitString(value, /[\r\n]/, 1)[0]
60
148
  return line.length > 0 ? line : fallback
61
149
  }
62
150
 
63
151
  export function oneLineMessage(error) {
64
- return firstLine(messageOf(error), 'Unknown error').replace(/\s+\(\d+:\d+\)$/, '')
152
+ return replaceString(firstLine(messageOf(error), 'Unknown error'), /\s+\(\d+:\d+\)$/, '')
65
153
  }
66
154
 
67
155
  /** Return DSH's reported missing-description property path when it is actionable. */
68
156
  export function missingDescriptionPath(error) {
69
- const violations = messageOf(error).matchAll(/missing required property ["']([^"']+)["']/g)
70
- for (const match of violations) {
157
+ const pattern = /missing required property ["']([^"']+)["']/g
158
+ const message = messageOf(error)
159
+ for (let match = regexpExec(pattern, message); match !== null; match = regexpExec(pattern, message)) {
71
160
  const path = match[1]
72
- if (path === 'description' || path.endsWith('.description')) return path
161
+ if (path === 'description' || endsWith(path, '.description')) return path
73
162
  }
74
163
  return undefined
75
164
  }
@@ -84,26 +173,34 @@ export function limitLogs(logs) {
84
173
  let bytes = 0
85
174
  const tail = []
86
175
  for (let index = logs.length - 1; index >= 0; index -= 1) {
87
- const size = Buffer.byteLength(logs[index], 'utf8') + 1
176
+ const size = bufferByteLength(logs[index], 'utf8') + 1
88
177
  if (bytes + size > MAX_ERROR_LOG_BYTES) break
89
178
  bytes += size
90
- tail.unshift(logs[index])
179
+ prependArray(tail, logs[index])
91
180
  }
92
181
  return tail
93
182
  }
94
183
 
95
- export function errorPosition(error, filename) {
184
+ /** A frame is only useful when its 1-based line and column are safe integers. */
185
+ function structuralPosition(position) {
186
+ return numberIsSafeInteger(position.line) && position.line >= 1
187
+ && numberIsSafeInteger(position.column) && position.column >= 1 ? position : undefined
188
+ }
189
+
190
+ /** Extract the first frame for the active cell. The frame is reported in the
191
+ * executed text, so it is only a cell position when it lies inside the source
192
+ * that produced that text. */
193
+ export function errorPosition(error, filename, source) {
96
194
  const stack = safeProperty(error, 'stack')
97
195
  if (typeof stack !== 'string') return undefined
98
- const escaped = filename.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
99
- const match = new RegExp(`${escaped}:(\\d+):(\\d+)`).exec(stack)
196
+ const escaped = replaceString(filename, /[.*+?^${}()|[\]\\]/g, '\\$&')
197
+ const match = regexpExec(new RegExp(`${escaped}:(\\d+):(\\d+)`), stack)
100
198
  if (match === null) return undefined
101
- const line = Number(match[1])
102
- const column = Number(match[2])
103
- return line >= 1 && column >= 1 ? { line, column } : undefined
199
+ const position = { line: toNumber(match[1]), column: toNumber(match[2]) }
200
+ return source === undefined ? structuralPosition(position) : cellPosition(position, source)
104
201
  }
105
202
 
106
- export function errorDetails(error, filename) {
203
+ export function errorDetails(error, filename, source) {
107
204
  const message = messageOf(error, 'Unprintable thrown value')
108
205
  const rawName = safeProperty(error, 'name')
109
206
  const name = typeof rawName === 'string' && rawName.length > 0 ? rawName : 'Error'
@@ -114,19 +211,30 @@ export function errorDetails(error, filename) {
114
211
  const candidate = safeProperty(error, 'ptcCause')
115
212
  const causeMessage = firstLine(safeProperty(candidate, 'message'))
116
213
  const causeCode = firstLine(safeProperty(candidate, 'code'))
214
+ const stderr = safeProperty(error, 'stderr')
215
+ const stderrText = typeof stderr === 'string' ? sliceString(stderr, 0, MAX_CAUSE_CODE_UNITS)
216
+ : bufferIsBuffer(stderr)
217
+ ? bufferToString(bufferSubarray(stderr, 0, MAX_CAUSE_CODE_UNITS), 'utf8') : ''
218
+ const boundedMessage = sliceString(message, 0, MAX_CAUSE_CODE_UNITS)
219
+ const continuation = searchString(boundedMessage, /[\r\n]/)
220
+ const detailParts = [continuation < 0 ? '' : sliceString(boundedMessage, continuation + 1), stderrText]
221
+ const detail = trimString(replaceString(
222
+ sliceString(join(detailParts, ' '), 0, MAX_CAUSE_CODE_UNITS), /\s+/g, ' '))
117
223
  const cause = causeMessage === undefined
118
- ? undefined
224
+ ? detail.length === 0 ? undefined : { message: detail }
119
225
  : {
120
226
  ...(causeCode === undefined ? {} : { code: causeCode }),
121
227
  message: causeMessage,
122
228
  }
123
- const position = errorPosition(error, filename)
229
+ const position = errorPosition(error, filename, source)
230
+ const failureOrigin = internal.weakMapGet(PROGRAM_FAILURES, error)
124
231
  return {
125
232
  name,
126
233
  message,
127
234
  ...(toolName === undefined ? {} : { toolName }),
128
235
  ...(position === undefined ? {} : { position }),
129
236
  ...(cause === undefined ? {} : { cause }),
237
+ ...(failureOrigin === undefined ? {} : { failureOrigin }),
130
238
  }
131
239
  }
132
240
 
@@ -140,16 +248,16 @@ export function createFailureTracker() {
140
248
  kind = undefined
141
249
  streak = 0
142
250
  },
143
- hint(error) {
251
+ hint(error, stateEffect = 'unknown') {
144
252
  const nextFingerprint = `${error.kind}\u0000${error.message}`
145
253
  if (fingerprint === nextFingerprint) streak += 1
146
254
  else {
147
255
  fingerprint = nextFingerprint
148
- kind = error[BINDING_FAILURE] === true ? 'binding' : 'generic'
256
+ kind = error[BINDING_FAILURE] ?? 'generic'
149
257
  streak = 1
150
258
  }
151
259
  if (streak !== FAILURE_HINT_THRESHOLD) return undefined
152
- return repeatedFailureDiagnostic(kind, streak)
260
+ return repeatedFailureDiagnostic(kind, streak, stateEffect)
153
261
  },
154
262
  })
155
263
  }
@@ -0,0 +1,66 @@
1
+ import { bindTypertRemote } from '@deepseek-ai/dsh-typert-protocol'
2
+ import { rpcDescriptor } from './rpc-contract.js'
3
+
4
+ /** Own Remote services and the package's Host invocation contribution. */
5
+ export function createHostRpc(ctx) {
6
+ const handlers = new Map()
7
+ let provider
8
+ let definition
9
+ let epoch
10
+ let disposed = false
11
+ const publish = () => {
12
+ // Registry withdrawal is synchronous, so replacement has no overlapping endpoints.
13
+ definition?.()
14
+ definition = undefined
15
+ if (provider === undefined || disposed || handlers.size === 0) return
16
+ definition = provider.effect(() => provider.typert.register({
17
+ package: 'dsh-ptc-plus', face: 'host', schemas: [],
18
+ model: { services: [], events: [], objects: [] },
19
+ invocations: [...handlers.keys()].map(rpcDescriptor),
20
+ }), 'ptc-plus: Remote definitions')
21
+ }
22
+ const port = {
23
+ register(contract, handler) {
24
+ if (disposed) throw new Error('PTC Plus RPC owner is disposed')
25
+ if (handlers.has(contract)) throw new Error(`PTC Plus RPC service ${contract.service} is already registered`)
26
+ const lifetime = new AbortController()
27
+ const service = {
28
+ invoke(operation, payload, signal) {
29
+ lifetime.signal.throwIfAborted()
30
+ if (epoch === undefined) throw new Error('PTC Plus RPC provider is unavailable')
31
+ return handler(operation, payload, AbortSignal.any([signal, lifetime.signal, epoch.signal]))
32
+ },
33
+ }
34
+ service.typertRemote = bindTypertRemote(service, contract.service)
35
+ const release = ctx.effect(function* () {
36
+ yield ctx.provide(contract.service, service)
37
+ handlers.set(contract, { release: () => release() })
38
+ yield () => {
39
+ lifetime.abort()
40
+ handlers.delete(contract)
41
+ publish()
42
+ }
43
+ publish()
44
+ }, 'ptc-plus: Remote service')
45
+ return release
46
+ },
47
+ }
48
+ const injection = ctx.inject?.(['typert'], scope => {
49
+ provider = scope
50
+ epoch = new AbortController()
51
+ publish()
52
+ scope.effect(() => scope.provide('ptcPlusRpc', port), 'ptc-plus: RPC registration port')
53
+ scope.effect(() => () => {
54
+ epoch.abort()
55
+ epoch = undefined
56
+ provider = undefined
57
+ definition = undefined
58
+ }, 'ptc-plus: RPC provider lifetime')
59
+ })
60
+ return { async dispose() {
61
+ disposed = true
62
+ for (const entry of [...handlers.values()]) await entry.release()
63
+ if (typeof injection === 'function') await injection()
64
+ else await injection?.dispose()
65
+ } }
66
+ }