dsh-ptc-plus 0.2.4 → 0.3.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 (49) hide show
  1. package/README.en.md +177 -0
  2. package/README.md +85 -69
  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-settings-en.png +0 -0
  7. package/assets/ptc-plus-settings-zh.png +0 -0
  8. package/client.js +1168 -144
  9. package/docs/adr/0006-rewrite-module-syntax-with-an-ast.md +1 -1
  10. package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +15 -3
  11. package/docs/adr/0013-isolate-repl-redeclaration-convenience.md +7 -3
  12. package/docs/adr/0014-persist-cell-rewrite-policy.md +7 -5
  13. package/docs/adr/0019-plugin-settings-and-kill-switch.md +19 -4
  14. package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
  15. package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +5 -3
  16. package/docs/adr/0022-stage-npm-releases-with-oidc.md +2 -2
  17. package/docs/architecture.md +23 -14
  18. package/docs/client-ui.md +29 -11
  19. package/docs/durability-design.md +42 -20
  20. package/docs/installation.md +11 -3
  21. package/docs/program-data-plane.md +6 -0
  22. package/docs/runtime-reference.md +27 -14
  23. package/docs/value-wire.md +1 -1
  24. package/index.js +38 -4
  25. package/internal/cell-analysis.js +89 -12
  26. package/internal/cell-rewriter.js +162 -4
  27. package/internal/config-spec.js +192 -117
  28. package/internal/direct-surface-owner.js +56 -5
  29. package/internal/edit-transport-owner.js +11 -2
  30. package/internal/failure-reporting.js +15 -0
  31. package/internal/kernel-worker.js +11 -0
  32. package/internal/rejected-cell-editor.js +21 -12
  33. package/internal/repl-convenience.js +94 -6
  34. package/internal/repl-memory-projection.js +277 -0
  35. package/internal/repl-rewrite-contract.js +15 -0
  36. package/internal/run-code-description.js +34 -0
  37. package/internal/runtime-bridge-owner.js +63 -15
  38. package/internal/runtime-config.js +3 -0
  39. package/internal/session-cell-executor.js +74 -14
  40. package/internal/session-events.js +7 -0
  41. package/internal/session-journal.js +331 -63
  42. package/internal/session-log-view.js +2 -1
  43. package/internal/session-runtime.js +110 -9
  44. package/internal/session-state.js +116 -4
  45. package/package.json +10 -7
  46. package/src/client-activity.js +501 -0
  47. package/src/client.js +456 -20
  48. package/README.zh.md +0 -161
  49. /package/assets/{ptc-plus-repair.png → ptc-plus-repair-zh.png} +0 -0
package/client.js CHANGED
@@ -12,14 +12,104 @@
12
12
  description: "",
13
13
  descriptionEn: ""
14
14
  },
15
+ {
16
+ key: "enhancedToolView",
17
+ type: "boolean",
18
+ default: true,
19
+ label: "\u4F7F\u7528 PTC Plus \u589E\u5F3A\u5DE5\u5177\u5361\u7247",
20
+ labelEn: "Use enhanced PTC Plus tool cards",
21
+ description: "\u5F00\u542F\u65F6\u4F7F\u7528\u589E\u5F3A\u5DE5\u5177\u5361\u7247\uFF1B\u5173\u95ED\u540E\u4F7F\u7528 DSH \u539F\u751F\u5DE5\u5177\u5361\u7247\u3002",
22
+ descriptionEn: "Uses enhanced tool cards when enabled and DSH native tool cards when disabled."
23
+ },
24
+ {
25
+ key: "autoDescribeRunCode",
26
+ type: "boolean",
27
+ default: true,
28
+ label: "\u5141\u8BB8\u6267\u884C\u7F3A\u5C11\u6458\u8981\u7684 run_code",
29
+ labelEn: "Allow run_code execution without a summary",
30
+ description: "\u5141\u8BB8 run_code \u7F3A\u5C11\u5916\u5C42 description\uFF1B\u5173\u95ED\u540E\u6062\u590D DSH \u6821\u9A8C\u3002\u6A21\u578B\u8BF7\u6C42\u548C\u539F\u59CB\u8C03\u7528\u53C2\u6570\u4FDD\u6301\u4E0D\u53D8\u3002",
31
+ descriptionEn: "Allows run_code to omit its outer description; disabling restores DSH validation. Model requests and original call arguments remain unchanged."
32
+ },
33
+ {
34
+ key: "canonicalizeToolCalls",
35
+ type: "boolean",
36
+ default: true,
37
+ label: "\u4FEE\u590D\u53EF\u8BC6\u522B\u7684\u9876\u5C42 native \u5DE5\u5177\u8BEF\u8C03\u7528",
38
+ labelEn: "Repair recognizable top-level native tool mis-calls",
39
+ description: "\u4EC5\u5F53\u5F53\u524D schema \u80FD\u552F\u4E00\u786E\u8BA4\u76EE\u6807\u5DE5\u5177\u4E14\u53C2\u6570\u6709\u6548\u65F6\uFF0C\u624D\u628A\u9876\u5C42 native \u5DE5\u5177\u8BEF\u8C03\u7528\u4FEE\u590D\u4E3A\u5BF9\u5E94\u7684 run_code \u8C03\u7528\u3002",
40
+ descriptionEn: "Repairs a top-level native tool mis-call as the corresponding run_code call only when the current schema uniquely identifies the tool and validates its arguments."
41
+ },
15
42
  {
16
43
  key: "cordisToolsEnabled",
17
44
  type: "boolean",
18
45
  default: false,
19
- label: "\u5728 PTC \u6A21\u5F0F\u4E2D\u542F\u7528 Cordis \u5DE5\u5177",
20
- labelEn: "Enable Cordis tools in PTC mode",
21
- description: "\u5373\u65F6\u4E3A PTC agent \u52A0\u5165\u6216\u79FB\u9664\u5B98\u65B9 Cordis \u5DE5\u5177\u3002",
22
- descriptionEn: "Adds or removes the official Cordis tools for the PTC agent immediately."
46
+ label: "\u5728 PTC \u6A21\u5F0F\u4E2D\u542F\u7528\u5B98\u65B9 Cordis \u5DE5\u5177",
47
+ labelEn: "Enable official Cordis tools in PTC mode",
48
+ description: "\u5F00\u542F\u540E\uFF0CPTC agent \u53EF\u4F7F\u7528\u5B98\u65B9 Cordis \u5DE5\u5177\u3001\u914D\u5957\u6307\u5F15\u548C\u5F00\u53D1 Skill\uFF1B\u5173\u95ED\u540E\u79FB\u9664\u8FD9\u4E9B\u80FD\u529B\u3002Cordis \u5DE5\u5177\u4EE5 DSH \u8FDB\u7A0B\u6743\u9650\u8FD0\u884C\u3002",
49
+ descriptionEn: "When enabled, PTC agents can use the official Cordis tools, guidance, and development Skill; disabling removes them. Cordis tools run with the DSH process permissions."
50
+ },
51
+ {
52
+ key: "looseTopLevelRedeclarations",
53
+ type: "boolean",
54
+ default: true,
55
+ label: "\u5141\u8BB8\u9876\u5C42\u53D8\u91CF\u91CD\u58F0\u660E",
56
+ labelEn: "Allow top-level variable redeclarations",
57
+ description: "\u5141\u8BB8\u9876\u5C42 const \u6216 let \u58F0\u660E\u66FF\u6362\u5DF2\u6709\u53D8\u91CF\uFF1B\u5173\u95ED\u540E\uFF0C\u91CD\u58F0\u660E\u4F1A\u5728\u6267\u884C\u524D\u88AB\u62D2\u7EDD\u3002",
58
+ descriptionEn: "Allows top-level const or let declarations to replace existing variables; disabling rejects redeclarations before execution."
59
+ },
60
+ {
61
+ key: "looseTopLevelFunctionClassRedeclarations",
62
+ type: "boolean",
63
+ default: false,
64
+ label: "\u5141\u8BB8\u9876\u5C42\u51FD\u6570/\u7C7B\u91CD\u58F0\u660E",
65
+ labelEn: "Allow top-level function/class redeclarations",
66
+ description: "\u5141\u8BB8\u9876\u5C42 function \u6216 class \u58F0\u660E\u66FF\u6362\u5DF2\u6709\u7684\u53EF\u5199 binding\uFF1B\u4E0D\u53EF\u5199\u6216\u4FDD\u7559 binding \u4ECD\u4F1A\u5728\u6267\u884C\u524D\u88AB\u62D2\u7EDD\u3002",
67
+ descriptionEn: "Allows top-level function or class declarations to replace existing writable bindings; immutable and reserved bindings are still rejected before execution."
68
+ },
69
+ {
70
+ key: "autoRewriteImports",
71
+ type: "boolean",
72
+ default: true,
73
+ label: "\u652F\u6301\u9759\u6001 import \u58F0\u660E",
74
+ labelEn: "Support static import declarations",
75
+ description: "\u5141\u8BB8\u5728 run_code \u4E2D\u4F7F\u7528\u9759\u6001 import\uFF0C\u5E76\u4FDD\u7559\u6A21\u5757\u89E3\u6790\u548C\u53EA\u8BFB binding \u8BED\u4E49\u3002",
76
+ descriptionEn: "Allows static import declarations in run_code while preserving module resolution and read-only binding semantics."
77
+ },
78
+ {
79
+ key: "autoStripExports",
80
+ type: "boolean",
81
+ default: true,
82
+ label: "\u652F\u6301\u9876\u5C42 export \u4FEE\u9970\u7B26",
83
+ labelEn: "Support top-level export modifiers",
84
+ description: "\u5141\u8BB8\u5728 run_code \u4E2D\u4F7F\u7528\u9876\u5C42 export\uFF0C\u5E76\u4FDD\u7559\u5BF9\u5E94\u7684\u672C\u5730\u58F0\u660E\u6216\u6A21\u5757\u526F\u4F5C\u7528\u3002",
85
+ descriptionEn: "Allows top-level export syntax in run_code while preserving the corresponding local declaration or module side effect."
86
+ },
87
+ {
88
+ key: "autoSplitRedeclarations",
89
+ type: "boolean",
90
+ default: true,
91
+ label: "\u652F\u6301\u6DF7\u5408\u89E3\u6784\u91CD\u58F0\u660E",
92
+ labelEn: "Support mixed destructuring redeclarations",
93
+ description: "\u5141\u8BB8\u9876\u5C42\u89E3\u6784\u58F0\u660E\u540C\u65F6\u5305\u542B\u65B0\u53D8\u91CF\u548C\u5DF2\u6709\u53D8\u91CF\u3002",
94
+ descriptionEn: "Allows top-level destructuring declarations to contain both new and existing variables."
95
+ },
96
+ {
97
+ key: "durableReplay",
98
+ type: "boolean",
99
+ default: true,
100
+ label: "\u542F\u7528\u6301\u4E45 REPL \u6062\u590D",
101
+ labelEn: "Enable durable REPL recovery",
102
+ description: "worker \u91CD\u542F\u540E\u4ECE session journal \u91CD\u5EFA\u53EF\u6062\u590D\u7684 REPL \u72B6\u6001\u3002",
103
+ descriptionEn: "Rebuilds recoverable REPL state from the session journal after a worker restart."
104
+ },
105
+ {
106
+ key: "tipsEnabled",
107
+ type: "boolean",
108
+ default: true,
109
+ label: "\u542F\u7528\u5931\u8D25\u6062\u590D\u63D0\u793A",
110
+ labelEn: "Enable failure recovery tips",
111
+ description: "\u5728\u7B26\u5408\u6761\u4EF6\u7684\u5931\u8D25\u540E\u663E\u793A\u6709\u51B7\u5374\u95F4\u9694\u7684\u6062\u590D\u63D0\u793A\u3002",
112
+ descriptionEn: "Shows recovery tips with a cooldown after qualifying failures."
23
113
  },
24
114
  {
25
115
  key: "computeMs",
@@ -27,10 +117,10 @@
27
117
  default: 6e4,
28
118
  min: 1,
29
119
  max: Number.MAX_SAFE_INTEGER,
30
- label: "\u5355 cell \u6700\u5927 CPU \u65F6\u95F4 (ms)",
31
- labelEn: "Max CPU time per cell (ms)",
32
- description: "\u540C\u6B65\u8BA1\u7B97\u8D85\u8FC7\u8BE5\u9884\u7B97\u7684 cell \u4F1A\u88AB\u4E2D\u65AD\u3002",
33
- descriptionEn: "Cells whose synchronous computation exceeds this budget are interrupted."
120
+ label: "\u5355 cell CPU \u65F6\u95F4\u4E0A\u9650 (ms)",
121
+ labelEn: "CPU time limit per cell (ms)",
122
+ description: "\u540C\u6B65\u8BA1\u7B97\u8D85\u8FC7\u8BE5\u4E0A\u9650\u65F6\u4E2D\u65AD\u5F53\u524D cell\u3002",
123
+ descriptionEn: "Interrupts the current cell when synchronous computation exceeds this limit."
34
124
  },
35
125
  {
36
126
  key: "maxWallMs",
@@ -38,32 +128,43 @@
38
128
  default: 6e5,
39
129
  min: 1,
40
130
  max: MAX_TIMER_DELAY_MS,
41
- label: "\u5355 cell \u6700\u5927\u5899\u949F\u65F6\u95F4 (ms)",
42
- labelEn: "Max wall-clock time per cell (ms)",
43
- description: "\u5B8C\u6574 cell \u6267\u884C\uFF08\u542B\u5F02\u6B65\u7B49\u5F85\uFF09\u7684\u6700\u957F\u8017\u65F6\u3002",
44
- descriptionEn: "The longest a full cell execution may take, including async waits."
131
+ label: "\u5355 cell \u603B\u8017\u65F6\u4E0A\u9650 (ms)",
132
+ labelEn: "Elapsed time limit per cell (ms)",
133
+ description: "\u9650\u5236\u5B8C\u6574 cell \u6267\u884C\u7684\u8017\u65F6\uFF0C\u5305\u62EC\u5F02\u6B65\u7B49\u5F85\u3002",
134
+ descriptionEn: "Limits the complete cell execution time, including asynchronous waits."
45
135
  },
46
136
  {
47
- key: "maxOutputBytes",
137
+ key: "maxOldGenerationSizeMb",
48
138
  type: "integer",
49
- default: 64 * 1024 * 1024,
139
+ default: 512,
50
140
  min: 1,
51
141
  max: Number.MAX_SAFE_INTEGER,
52
- label: "\u6700\u5927\u8F93\u51FA\u5B57\u8282",
53
- labelEn: "Max output bytes",
54
- description: "\u9650\u5236\u5355\u4E2A cell \u7684\u8F93\u51FA\u548C\u7ED3\u679C\u6570\u636E\u603B\u5927\u5C0F\u3002",
55
- descriptionEn: "Caps the combined size of one cell output and result data."
142
+ label: "\u6BCF\u4E2A worker \u65E7\u751F\u4EE3\u5185\u5B58\u4E0A\u9650 (MiB)",
143
+ labelEn: "Old-generation memory limit per worker (MiB)",
144
+ description: "\u9650\u5236\u6BCF\u4E2A worker \u7684 V8 \u65E7\u751F\u4EE3\uFF1B\u6D3B\u52A8 worker \u5B58\u5728\u65F6\u4E0D\u80FD\u4FEE\u6539\u3002",
145
+ descriptionEn: "Limits the V8 old generation for each worker; this setting cannot change while a worker is active."
56
146
  },
57
147
  {
58
- key: "maxOldGenerationSizeMb",
148
+ key: "maxNestedRunCodeDepth",
59
149
  type: "integer",
60
- default: 512,
150
+ default: 8,
151
+ min: 1,
152
+ max: Number.MAX_SAFE_INTEGER,
153
+ label: "code.run \u5D4C\u5957\u5C42\u6570\u4E0A\u9650",
154
+ labelEn: "code.run nesting depth limit",
155
+ description: "\u9650\u5236\u5355\u4E2A\u9876\u5C42 cell \u4E2D code.run \u7684\u6700\u5927\u5D4C\u5957\u5C42\u6570\u3002",
156
+ descriptionEn: "Limits the maximum code.run nesting depth within one top-level cell."
157
+ },
158
+ {
159
+ key: "maxOutputBytes",
160
+ type: "integer",
161
+ default: 64 * 1024 * 1024,
61
162
  min: 1,
62
163
  max: Number.MAX_SAFE_INTEGER,
63
- label: "worker \u65E7\u751F\u4EE3\u5185\u5B58\u4E0A\u9650 (MiB)",
64
- labelEn: "Worker old-generation memory cap (MiB)",
65
- description: "\u6BCF\u4E2A worker \u7684 V8 \u65E7\u751F\u4EE3\u4E0A\u9650\uFF1B\u6D3B\u52A8 worker \u5B58\u5728\u65F6\u4FEE\u6539\u4F1A\u88AB\u62D2\u7EDD\u3002",
66
- descriptionEn: "Per-worker V8 old-generation limit; changes are rejected while an active worker exists."
164
+ label: "\u5355 cell \u8F93\u51FA\u4E0A\u9650 (bytes)",
165
+ labelEn: "Output limit per cell (bytes)",
166
+ description: "\u9650\u5236\u5355\u4E2A cell \u7684\u65E5\u5FD7\u548C\u8FD4\u56DE\u7ED3\u679C\u7684\u5408\u8BA1\u5927\u5C0F\u3002",
167
+ descriptionEn: "Limits the combined size of one cell's logs and returned result."
67
168
  },
68
169
  {
69
170
  key: "maxValueNodes",
@@ -71,10 +172,10 @@
71
172
  default: 1e5,
72
173
  min: 1,
73
174
  max: Number.MAX_SAFE_INTEGER,
74
- label: "Value Graph \u6700\u5927\u8282\u70B9\u6570",
75
- labelEn: "Max Value Graph nodes",
76
- description: "\u9650\u5236\u5355\u6B21\u8FD4\u56DE\u503C\u7684\u8282\u70B9\u6570\u3002",
77
- descriptionEn: "Caps the node count of a single returned value."
175
+ label: "\u8FD4\u56DE\u503C\u8282\u70B9\u6570\u4E0A\u9650",
176
+ labelEn: "Returned-value node limit",
177
+ description: "\u9650\u5236\u5355\u6B21\u8FD4\u56DE\u503C\u7684 Value Graph \u8282\u70B9\u6570\u3002",
178
+ descriptionEn: "Limits the Value Graph node count for one returned value."
78
179
  },
79
180
  {
80
181
  key: "maxValueEdges",
@@ -82,10 +183,10 @@
82
183
  default: 1e6,
83
184
  min: 1,
84
185
  max: Number.MAX_SAFE_INTEGER,
85
- label: "Value Graph \u6700\u5927\u8FB9\u6570",
86
- labelEn: "Max Value Graph edges",
87
- description: "\u9650\u5236\u5355\u6B21\u8FD4\u56DE\u503C\u7684\u5F15\u7528\u5173\u7CFB\u6570\u3002",
88
- descriptionEn: "Caps the reference-edge count of a single returned value."
186
+ label: "\u8FD4\u56DE\u503C\u5F15\u7528\u8FB9\u6570\u4E0A\u9650",
187
+ labelEn: "Returned-value reference-edge limit",
188
+ description: "\u9650\u5236\u5355\u6B21\u8FD4\u56DE\u503C\u7684 Value Graph \u5F15\u7528\u8FB9\u6570\u3002",
189
+ descriptionEn: "Limits the Value Graph reference-edge count for one returned value."
89
190
  },
90
191
  {
91
192
  key: "maxValueArrayLength",
@@ -93,10 +194,10 @@
93
194
  default: 1e6,
94
195
  min: 1,
95
196
  max: Number.MAX_SAFE_INTEGER,
96
- label: "\u6570\u7EC4\u6700\u5927\u58F0\u660E\u957F\u5EA6",
97
- labelEn: "Max declared array length",
98
- description: "\u9650\u5236\u8FD4\u56DE\u6570\u7EC4\u7684\u6700\u5927\u957F\u5EA6\u3002",
99
- descriptionEn: "Caps the maximum length of a returned array."
197
+ label: "\u8FD4\u56DE\u6570\u7EC4\u957F\u5EA6\u4E0A\u9650",
198
+ labelEn: "Returned-array length limit",
199
+ description: "\u9650\u5236\u8FD4\u56DE\u6570\u7EC4\u7684\u6700\u5927\u58F0\u660E\u957F\u5EA6\u3002",
200
+ descriptionEn: "Limits the maximum declared length of a returned array."
100
201
  },
101
202
  {
102
203
  key: "maxValueBigIntDigits",
@@ -104,84 +205,10 @@
104
205
  default: 1e5,
105
206
  min: 1,
106
207
  max: Number.MAX_SAFE_INTEGER,
107
- label: "BigInt \u6700\u5927\u5341\u8FDB\u5236\u4F4D\u6570",
108
- labelEn: "Max BigInt decimal digits",
208
+ label: "\u8FD4\u56DE\u503C BigInt \u5341\u8FDB\u5236\u4F4D\u6570\u4E0A\u9650",
209
+ labelEn: "Returned-value BigInt decimal-digit limit",
109
210
  description: "\u9650\u5236\u8FD4\u56DE\u503C\u4E2D BigInt \u7684\u5341\u8FDB\u5236\u4F4D\u6570\u3002",
110
- descriptionEn: "Caps the decimal digits of BigInt values in a returned value."
111
- },
112
- {
113
- key: "maxNestedRunCodeDepth",
114
- type: "integer",
115
- default: 8,
116
- min: 1,
117
- max: Number.MAX_SAFE_INTEGER,
118
- label: "code.run \u6700\u5927\u9012\u5F52\u6DF1\u5EA6",
119
- labelEn: "Max code.run recursion depth",
120
- description: "\u9650\u5236 code.run \u7684\u5D4C\u5957\u5C42\u6570\u3002",
121
- descriptionEn: "Caps how deeply code.run may nest."
122
- },
123
- {
124
- key: "canonicalizeToolCalls",
125
- type: "boolean",
126
- default: true,
127
- label: "\u89C4\u8303\u9876\u5C42 native \u8BEF\u8C03",
128
- labelEn: "Canonicalize top-level native mis-calls",
129
- description: "\u4FEE\u6B63\u53EF\u4EE5\u660E\u786E\u8BC6\u522B\u7684\u9876\u5C42 native \u8BEF\u8C03\u3002",
130
- descriptionEn: "Repairs top-level native mis-calls that can be identified unambiguously."
131
- },
132
- {
133
- key: "looseTopLevelRedeclarations",
134
- type: "boolean",
135
- default: true,
136
- label: "\u5BBD\u677E\u9876\u5C42\u91CD\u58F0\u660E",
137
- labelEn: "Loose top-level redeclarations",
138
- description: "\u5141\u8BB8\u9876\u5C42 const/let \u91CD\u58F0\u660E\u5DF2\u6709\u53D8\u91CF\u3002",
139
- descriptionEn: "Allows top-level const/let to redeclare existing variables."
140
- },
141
- {
142
- key: "durableReplay",
143
- type: "boolean",
144
- default: true,
145
- label: "\u6301\u4E45\u91CD\u653E",
146
- labelEn: "Durable replay",
147
- description: "worker \u91CD\u542F\u540E\u6062\u590D\u53EF\u4EE5\u91CD\u5EFA\u7684 REPL \u72B6\u6001\u3002",
148
- descriptionEn: "Rebuilds recoverable REPL state after a worker restart."
149
- },
150
- {
151
- key: "autoRewriteImports",
152
- type: "boolean",
153
- default: true,
154
- label: "\u81EA\u52A8\u6539\u5199 import",
155
- labelEn: "Auto-rewrite import",
156
- description: "\u5141\u8BB8\u5728 run_code \u4E2D\u4F7F\u7528\u9759\u6001 import\u3002",
157
- descriptionEn: "Allows static import declarations inside run_code."
158
- },
159
- {
160
- key: "autoStripExports",
161
- type: "boolean",
162
- default: true,
163
- label: "\u81EA\u52A8\u5265\u79BB export",
164
- labelEn: "Auto-strip export",
165
- description: "\u5141\u8BB8\u5728 run_code \u4E2D\u4F7F\u7528\u9876\u5C42 export\u3002",
166
- descriptionEn: "Allows top-level export modifiers inside run_code."
167
- },
168
- {
169
- key: "autoSplitRedeclarations",
170
- type: "boolean",
171
- default: true,
172
- label: "\u81EA\u52A8\u62C6\u5206\u6DF7\u5408\u91CD\u58F0\u660E",
173
- labelEn: "Auto-split mixed redeclarations",
174
- description: "\u5141\u8BB8\u9876\u5C42\u89E3\u6784\u58F0\u660E\u540C\u65F6\u5305\u542B\u65B0\u65E7\u53D8\u91CF\u3002",
175
- descriptionEn: "Allows top-level destructuring declarations that mix new and existing variables."
176
- },
177
- {
178
- key: "tipsEnabled",
179
- type: "boolean",
180
- default: true,
181
- label: "\u542F\u7528\u6062\u590D\u63D0\u793A",
182
- labelEn: "Enable recovery tips",
183
- description: "\u5728\u7B26\u5408\u6761\u4EF6\u7684\u5931\u8D25\u540E\u663E\u793A\u6062\u590D\u63D0\u793A\u3002",
184
- descriptionEn: "Shows recovery tips after qualifying failures."
211
+ descriptionEn: "Limits the decimal-digit count of BigInt values in one returned value."
185
212
  },
186
213
  {
187
214
  key: "tipCooldownMessages",
@@ -189,8 +216,8 @@
189
216
  default: 3,
190
217
  min: 1,
191
218
  max: Number.MAX_SAFE_INTEGER,
192
- label: "\u6062\u590D\u63D0\u793A\u51B7\u5374\u6B65\u6570",
193
- labelEn: "Recovery tip cooldown (messages)",
219
+ label: "\u540C\u7C7B\u6062\u590D\u63D0\u793A\u95F4\u9694 (messages)",
220
+ labelEn: "Same-kind recovery tip interval (messages)",
194
221
  description: "\u540C\u7C7B\u6062\u590D\u63D0\u793A\u4E4B\u95F4\u7684\u6700\u5C0F\u95F4\u9694\u3002",
195
222
  descriptionEn: "The minimum gap between recovery tips of the same kind."
196
223
  },
@@ -200,16 +227,526 @@
200
227
  default: 2,
201
228
  min: 1,
202
229
  max: Number.MAX_SAFE_INTEGER,
203
- label: "\u6062\u590D\u63D0\u793A\u5347\u7EA7\u5931\u8D25\u6B21\u6570",
204
- labelEn: "Recovery tip escalation failures",
230
+ label: "\u8BE6\u7EC6\u6062\u590D\u63D0\u793A\u9608\u503C (failures)",
231
+ labelEn: "Detailed recovery tip threshold (failures)",
205
232
  description: "\u8FDE\u7EED\u5931\u8D25\u8FBE\u5230\u6B64\u6B21\u6570\u540E\u663E\u793A\u66F4\u8BE6\u7EC6\u7684\u6062\u590D\u63D0\u793A\u3002",
206
233
  descriptionEn: "After this many consecutive failures, a more detailed recovery tip is shown."
207
234
  }
208
235
  ]);
236
+ var CONFIG_GROUPS = Object.freeze([
237
+ {
238
+ key: "core",
239
+ label: "\u5E38\u7528\u4E0E\u517C\u5BB9\u6027",
240
+ labelEn: "Common and compatibility",
241
+ fields: Object.freeze(["enabled", "enhancedToolView", "autoDescribeRunCode", "canonicalizeToolCalls"])
242
+ },
243
+ {
244
+ key: "optional",
245
+ label: "\u53EF\u9009\u80FD\u529B",
246
+ labelEn: "Optional capabilities",
247
+ fields: Object.freeze(["cordisToolsEnabled"])
248
+ },
249
+ {
250
+ key: "advanced",
251
+ label: "\u9AD8\u7EA7\u884C\u4E3A",
252
+ labelEn: "Advanced behavior",
253
+ fields: Object.freeze([
254
+ "looseTopLevelRedeclarations",
255
+ "looseTopLevelFunctionClassRedeclarations",
256
+ "autoRewriteImports",
257
+ "autoStripExports",
258
+ "autoSplitRedeclarations",
259
+ "durableReplay",
260
+ "tipsEnabled"
261
+ ])
262
+ },
263
+ {
264
+ key: "limits",
265
+ label: "\u8D44\u6E90\u9650\u5236",
266
+ labelEn: "Resource limits",
267
+ fields: Object.freeze([
268
+ "computeMs",
269
+ "maxWallMs",
270
+ "maxOldGenerationSizeMb",
271
+ "maxNestedRunCodeDepth",
272
+ "maxOutputBytes",
273
+ "maxValueNodes",
274
+ "maxValueEdges",
275
+ "maxValueArrayLength",
276
+ "maxValueBigIntDigits",
277
+ "tipCooldownMessages",
278
+ "tipEscalationFailures"
279
+ ])
280
+ }
281
+ ]);
209
282
  var CONFIG_DEFAULTS = Object.freeze(
210
283
  Object.fromEntries(CONFIG_FIELDS.map((field) => [field.key, field.default]))
211
284
  );
212
285
 
286
+ // src/client-activity.js
287
+ var JOURNAL_VERSIONS = /* @__PURE__ */ new Set([1, 2, 3, 4]);
288
+ var JOURNAL_STATUSES = /* @__PURE__ */ new Set(["durable", "volatile", "discarded", "noop"]);
289
+ var BINDING_MODES = /* @__PURE__ */ new Set(["loose", "strict"]);
290
+ var JOURNAL_FIELDS = /* @__PURE__ */ new Set([
291
+ "version",
292
+ "bindingPolicy",
293
+ "rewritePolicy",
294
+ "moduleSemantics",
295
+ "status",
296
+ "calls",
297
+ "operations",
298
+ "confirms",
299
+ "diagnostics",
300
+ "completion",
301
+ "volatileReason"
302
+ ]);
303
+ var PREDECESSOR_JOURNAL_FIELDS = /* @__PURE__ */ new Set([
304
+ "version",
305
+ "bindingMode",
306
+ "rewritePolicy",
307
+ "status",
308
+ "calls",
309
+ "operations",
310
+ "confirms",
311
+ "diagnostics",
312
+ "completion",
313
+ "volatileReason"
314
+ ]);
315
+ var LEGACY_JOURNAL_FIELDS = new Set([...PREDECESSOR_JOURNAL_FIELDS].filter((key) => key !== "rewritePolicy"));
316
+ var BINDING_POLICY_FIELDS = /* @__PURE__ */ new Set(["variableRedeclarations", "functionClassRedeclarations"]);
317
+ var REWRITE_POLICY_FIELDS = /* @__PURE__ */ new Set([
318
+ "autoRewriteImports",
319
+ "autoStripExports",
320
+ "autoSplitRedeclarations"
321
+ ]);
322
+ var MODULE_SEMANTICS_FIELDS = /* @__PURE__ */ new Set(["defaultExportBinding"]);
323
+ var DEFAULT_EXPORT_BINDINGS = /* @__PURE__ */ new Set(["legacy-variable", "live-readonly"]);
324
+ var CALL_SUCCESS_FIELDS = /* @__PURE__ */ new Set(["global", "member", "args", "ok", "value", "settle"]);
325
+ var CALL_ERROR_FIELDS = /* @__PURE__ */ new Set(["global", "member", "args", "ok", "error", "settle"]);
326
+ var OPERATION_FIELDS = /* @__PURE__ */ new Set(["action", "name"]);
327
+ var RETURN_FIELDS = /* @__PURE__ */ new Set(["kind", "hasValue", "value"]);
328
+ var THROW_FIELDS = /* @__PURE__ */ new Set(["kind", "error"]);
329
+ var ERROR_FIELDS = /* @__PURE__ */ new Set(["kind", "message"]);
330
+ var DIAGNOSTIC_FIELDS = /* @__PURE__ */ new Set([
331
+ "code",
332
+ "severity",
333
+ "phase",
334
+ "message",
335
+ "stateEffect",
336
+ "dispatchState",
337
+ "source",
338
+ "cause",
339
+ "help"
340
+ ]);
341
+ var SOURCE_FIELDS = /* @__PURE__ */ new Set(["cell", "start", "end"]);
342
+ var POSITION_FIELDS = /* @__PURE__ */ new Set(["line", "column"]);
343
+ var CAUSE_FIELDS = /* @__PURE__ */ new Set(["code", "message"]);
344
+ var SEVERITIES = /* @__PURE__ */ new Set(["error", "warning", "note"]);
345
+ var PHASES = /* @__PURE__ */ new Set(["parse", "preflight", "execute", "tool-dispatch", "replay", "recover"]);
346
+ var STATE_EFFECTS = /* @__PURE__ */ new Set(["unchanged", "partially-applied", "rolled-back", "unknown"]);
347
+ var DISPATCH_STATES = /* @__PURE__ */ new Set(["not-dispatched", "dispatched", "completed", "unknown"]);
348
+ var REWRITE_FIELDS = /* @__PURE__ */ new Set(["kind", "description", "source"]);
349
+ var REWRITE_KINDS = /* @__PURE__ */ new Set(["import", "redeclaration", "export"]);
350
+ var REWRITE_POLICY_BY_KIND = Object.freeze({
351
+ import: "autoRewriteImports",
352
+ export: "autoStripExports"
353
+ });
354
+ var MIXED_REDECLARATION = "split a mixed top-level declaration while preserving native pattern initialization";
355
+ var FUNCTION_REDECLARATION = "reassigned an existing top-level function declaration for REPL continuity";
356
+ var CLASS_REDECLARATION = "reassigned an existing top-level class declaration for REPL continuity";
357
+ var EDIT_TARGET_FIELDS = /* @__PURE__ */ new Set(["targetCallSeq"]);
358
+ var DERIVED_RUN_FIELDS = /* @__PURE__ */ new Set(["code", "description"]);
359
+ var RECOVERY_BOUNDARY_FIELDS = /* @__PURE__ */ new Set(["failedCallSeq", "frontierCallSeq"]);
360
+ var VALUE_ENVELOPE_FIELDS = /* @__PURE__ */ new Set(["codec", "root", "nodes"]);
361
+ var VALUE_OBJECT_FIELDS = /* @__PURE__ */ new Set(["type", "prototype", "entries"]);
362
+ var VALUE_ARRAY_FIELDS = /* @__PURE__ */ new Set(["type", "length", "entries"]);
363
+ var VALUE_UNDEFINED_FIELDS = /* @__PURE__ */ new Set(["tag"]);
364
+ var VALUE_NUMBER_FIELDS = /* @__PURE__ */ new Set(["tag", "value"]);
365
+ var VALUE_BIGINT_FIELDS = /* @__PURE__ */ new Set(["tag", "value"]);
366
+ var VALUE_REFERENCE_FIELDS = /* @__PURE__ */ new Set(["tag", "index"]);
367
+ var MAX_VALUE_NODES = 1e5;
368
+ var MAX_VALUE_EDGES = 1e6;
369
+ var MAX_ARRAY_LENGTH = 1e6;
370
+ var MAX_BIGINT_DIGITS = 1e5;
371
+ var MAX_STRING_BYTES = 64 * 1024 * 1024;
372
+ var textEncoder = new TextEncoder();
373
+ function isRecord(value) {
374
+ return value !== null && typeof value === "object" && !Array.isArray(value);
375
+ }
376
+ function hasClosedFields(value, allowed, required = []) {
377
+ if (!isRecord(value)) return false;
378
+ const keys = Reflect.ownKeys(value);
379
+ if (keys.some((key) => typeof key !== "string" || !allowed.has(key) || !Object.prototype.propertyIsEnumerable.call(value, key))) return false;
380
+ return [...required].every((key) => Object.hasOwn(value, key));
381
+ }
382
+ function hasExactFields(value, fields) {
383
+ return hasClosedFields(value, fields, fields) && Reflect.ownKeys(value).length === fields.size;
384
+ }
385
+ function hasExactOrderedFields(value, fields) {
386
+ if (!isRecord(value)) return false;
387
+ const keys = Reflect.ownKeys(value);
388
+ const ordered = [...fields];
389
+ return keys.length === ordered.length && keys.every((key, index) => key === ordered[index] && Object.prototype.propertyIsEnumerable.call(value, key));
390
+ }
391
+ function isLine(value) {
392
+ return typeof value === "string" && value.length > 0 && !/[\r\n]/.test(value);
393
+ }
394
+ function isSafeSequence(value) {
395
+ return Number.isSafeInteger(value) && value >= 0;
396
+ }
397
+ function isValidPosition(value) {
398
+ return hasExactFields(value, POSITION_FIELDS) && Number.isSafeInteger(value.line) && value.line >= 1 && Number.isSafeInteger(value.column) && value.column >= 1;
399
+ }
400
+ function isValidDiagnosticCause(value, depth) {
401
+ if (depth > 16 || !isRecord(value)) return false;
402
+ if (Object.hasOwn(value, "severity")) return isValidDiagnostic(value, depth + 1);
403
+ return hasClosedFields(value, CAUSE_FIELDS, ["message"]) && (value.code === void 0 || isLine(value.code)) && isLine(value.message);
404
+ }
405
+ function isValidDiagnostic(value, depth = 0) {
406
+ if (!hasClosedFields(value, DIAGNOSTIC_FIELDS, [
407
+ "code",
408
+ "severity",
409
+ "phase",
410
+ "message",
411
+ "stateEffect"
412
+ ])) return false;
413
+ if (typeof value.code !== "string" || !/^[A-Z][A-Z0-9-]{2,31}$/.test(value.code) || !SEVERITIES.has(value.severity) || !PHASES.has(value.phase) || !STATE_EFFECTS.has(value.stateEffect) || !isLine(value.message)) return false;
414
+ if (value.dispatchState !== void 0 && !DISPATCH_STATES.has(value.dispatchState)) return false;
415
+ if (value.source !== void 0) {
416
+ if (!hasClosedFields(value.source, SOURCE_FIELDS, ["cell", "start"]) || !isLine(value.source.cell) || !isValidPosition(value.source.start) || value.source.end !== void 0 && !isValidPosition(value.source.end)) return false;
417
+ if (value.source.end !== void 0 && (value.source.end.line < value.source.start.line || value.source.end.line === value.source.start.line && value.source.end.column < value.source.start.column)) return false;
418
+ }
419
+ if (value.cause !== void 0 && !isValidDiagnosticCause(value.cause, depth)) return false;
420
+ return value.help === void 0 || Array.isArray(value.help) && value.help.length <= 3 && value.help.every(isLine);
421
+ }
422
+ function isArrayIndexKey(value) {
423
+ if (!/^(0|[1-9][0-9]*)$/.test(value)) return false;
424
+ const parsed = Number(value);
425
+ return Number.isInteger(parsed) && parsed >= 0 && parsed < 4294967295 && String(parsed) === value;
426
+ }
427
+ function accountText(value, state) {
428
+ state.textBytes += textEncoder.encode(value).byteLength;
429
+ return state.textBytes <= MAX_STRING_BYTES;
430
+ }
431
+ function isValidValueAtom(value, state) {
432
+ if (value === null || typeof value === "boolean") return true;
433
+ if (typeof value === "string") return accountText(value, state);
434
+ if (typeof value === "number") return Number.isFinite(value) && !Object.is(value, -0);
435
+ if (!isRecord(value) || typeof value.tag !== "string") return false;
436
+ if (value.tag === "undefined") return hasExactOrderedFields(value, VALUE_UNDEFINED_FIELDS);
437
+ if (value.tag === "number") {
438
+ return hasExactOrderedFields(value, VALUE_NUMBER_FIELDS) && ["nan", "infinity", "-infinity", "-0"].includes(value.value);
439
+ }
440
+ if (value.tag === "bigint") {
441
+ if (!hasExactOrderedFields(value, VALUE_BIGINT_FIELDS) || typeof value.value !== "string" || !/^(0|-[1-9][0-9]*|[1-9][0-9]*)$/.test(value.value)) return false;
442
+ const digits = value.value[0] === "-" ? value.value.length - 1 : value.value.length;
443
+ return digits <= MAX_BIGINT_DIGITS && accountText(value.value, state);
444
+ }
445
+ if (value.tag !== "reference" || !hasExactOrderedFields(value, VALUE_REFERENCE_FIELDS) || !isSafeSequence(value.index) || value.index >= state.nodeCount) return false;
446
+ if (!state.discovered.has(value.index)) {
447
+ if (value.index !== state.discovered.size) return false;
448
+ state.discovered.add(value.index);
449
+ }
450
+ return true;
451
+ }
452
+ function isValidValueWire(value) {
453
+ if (!hasExactOrderedFields(value, VALUE_ENVELOPE_FIELDS) || value.codec !== "ptc-value-graph/v1" || !Array.isArray(value.nodes) || value.nodes.length > MAX_VALUE_NODES) return false;
454
+ const state = {
455
+ nodeCount: value.nodes.length,
456
+ discovered: /* @__PURE__ */ new Set(),
457
+ edges: 0,
458
+ textBytes: 0
459
+ };
460
+ if (!isValidValueAtom(value.root, state)) return false;
461
+ for (const [nodeIndex, node] of value.nodes.entries()) {
462
+ if (!state.discovered.has(nodeIndex) || !isRecord(node)) return false;
463
+ if (node.type === "array") {
464
+ if (!hasExactOrderedFields(node, VALUE_ARRAY_FIELDS) || !Number.isSafeInteger(node.length) || node.length < 0 || node.length > MAX_ARRAY_LENGTH || !Array.isArray(node.entries)) return false;
465
+ let previous = -1;
466
+ for (const entry of node.entries) {
467
+ if (!Array.isArray(entry) || entry.length !== 2 || !Number.isSafeInteger(entry[0]) || entry[0] <= previous || entry[0] < 0 || entry[0] >= node.length) return false;
468
+ previous = entry[0];
469
+ state.edges += 1;
470
+ if (state.edges > MAX_VALUE_EDGES || !isValidValueAtom(entry[1], state)) return false;
471
+ }
472
+ continue;
473
+ }
474
+ if (node.type !== "object" || !hasExactOrderedFields(node, VALUE_OBJECT_FIELDS) || !["object", "null"].includes(node.prototype) || !Array.isArray(node.entries)) return false;
475
+ const keys = /* @__PURE__ */ new Set();
476
+ let previousArrayIndex = -1;
477
+ let sawOtherKey = false;
478
+ for (const entry of node.entries) {
479
+ if (!Array.isArray(entry) || entry.length !== 2 || typeof entry[0] !== "string" || keys.has(entry[0]) || !accountText(entry[0], state)) return false;
480
+ keys.add(entry[0]);
481
+ if (isArrayIndexKey(entry[0])) {
482
+ const current = Number(entry[0]);
483
+ if (sawOtherKey || current <= previousArrayIndex) return false;
484
+ previousArrayIndex = current;
485
+ } else {
486
+ sawOtherKey = true;
487
+ }
488
+ state.edges += 1;
489
+ if (state.edges > MAX_VALUE_EDGES || !isValidValueAtom(entry[1], state)) return false;
490
+ }
491
+ }
492
+ return state.discovered.size === value.nodes.length;
493
+ }
494
+ function isValidRewritePolicy(value) {
495
+ return hasExactFields(value, REWRITE_POLICY_FIELDS) && [...REWRITE_POLICY_FIELDS].every((key) => typeof value[key] === "boolean");
496
+ }
497
+ function isValidBindingPolicy(value) {
498
+ return hasExactFields(value, BINDING_POLICY_FIELDS) && [...BINDING_POLICY_FIELDS].every((key) => typeof value[key] === "boolean");
499
+ }
500
+ function isValidModuleSemantics(value) {
501
+ return hasExactFields(value, MODULE_SEMANTICS_FIELDS) && DEFAULT_EXPORT_BINDINGS.has(value.defaultExportBinding);
502
+ }
503
+ function isValidCall(value) {
504
+ if (!isRecord(value) || value.ok !== true && value.ok !== false) return false;
505
+ const fields = value.ok ? CALL_SUCCESS_FIELDS : CALL_ERROR_FIELDS;
506
+ if (!hasExactFields(value, fields) || typeof value.global !== "string" || typeof value.member !== "string" || !isValidValueWire(value.args) || !isSafeSequence(value.settle)) return false;
507
+ return value.ok ? isValidValueWire(value.value) : typeof value.error === "string";
508
+ }
509
+ function isValidOperation(value) {
510
+ if (!hasClosedFields(value, OPERATION_FIELDS, ["action"]) || !["save", "restore", "delete"].includes(value.action)) return false;
511
+ if (value.action === "restore" && value.name === void 0) return true;
512
+ return typeof value.name === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(value.name);
513
+ }
514
+ function isValidCompletion(value) {
515
+ if (!isRecord(value) || !["return", "throw"].includes(value.kind)) return false;
516
+ if (value.kind === "return") {
517
+ if (!hasClosedFields(value, RETURN_FIELDS, ["kind", "hasValue"]) || typeof value.hasValue !== "boolean") return false;
518
+ return value.hasValue ? Object.hasOwn(value, "value") && isValidValueWire(value.value) : !Object.hasOwn(value, "value");
519
+ }
520
+ return hasExactFields(value, THROW_FIELDS) && hasExactFields(value.error, ERROR_FIELDS) && typeof value.error.kind === "string" && typeof value.error.message === "string";
521
+ }
522
+ function isValidConfirms(value, version) {
523
+ if (value === void 0) return true;
524
+ if (!Array.isArray(value) || new Set(value).size !== value.length) return false;
525
+ if (version === 1) return value.length === 0;
526
+ return value.every(isSafeSequence);
527
+ }
528
+ function isReadableJournalUnchecked(value) {
529
+ if (!isRecord(value) || !JOURNAL_VERSIONS.has(value.version) || !JOURNAL_STATUSES.has(value.status)) {
530
+ return false;
531
+ }
532
+ const predecessor = value.version !== 4;
533
+ const fields = value.version === 1 ? LEGACY_JOURNAL_FIELDS : predecessor ? PREDECESSOR_JOURNAL_FIELDS : JOURNAL_FIELDS;
534
+ const required = predecessor ? ["version", "bindingMode", "status", "calls", "operations", "diagnostics"] : ["version", "bindingPolicy", "rewritePolicy", "moduleSemantics", "status", "calls", "operations", "diagnostics"];
535
+ if (value.version !== 1 && value.version !== 4) required.push("rewritePolicy");
536
+ if (!hasClosedFields(value, fields, required) || predecessor && !BINDING_MODES.has(value.bindingMode) || value.version === 4 && !isValidBindingPolicy(value.bindingPolicy) || value.version === 4 && !isValidModuleSemantics(value.moduleSemantics) || value.version !== 1 && !isValidRewritePolicy(value.rewritePolicy) || !Array.isArray(value.calls) || !value.calls.every(isValidCall) || !Array.isArray(value.operations) || !value.operations.every(isValidOperation) || !isValidConfirms(value.confirms, value.version) || !Array.isArray(value.diagnostics) || !value.diagnostics.every(isValidDiagnostic)) return false;
537
+ const settlementOrder = value.calls.map((call) => call.settle).sort((left, right) => left - right);
538
+ if (settlementOrder.some((settle, index) => settle !== index)) return false;
539
+ const requiresCompletion = value.status === "durable" || value.status === "volatile";
540
+ if (requiresCompletion && !isValidCompletion(value.completion) || !requiresCompletion && value.completion !== void 0 && !isValidCompletion(value.completion)) return false;
541
+ if ((value.status === "discarded" || value.status === "noop") && (value.calls.length !== 0 || value.operations.length !== 0)) return false;
542
+ if (value.volatileReason !== void 0 && (typeof value.volatileReason !== "string" || value.status !== "volatile" && value.status !== "discarded")) return false;
543
+ return true;
544
+ }
545
+ function isReadableJournal(value) {
546
+ try {
547
+ return isReadableJournalUnchecked(value);
548
+ } catch {
549
+ return false;
550
+ }
551
+ }
552
+ function isCountableJournal(value) {
553
+ return isReadableJournal(value) && JOURNAL_STATUSES.has(value.status) && Array.isArray(value.calls) && Array.isArray(value.operations) && Array.isArray(value.diagnostics);
554
+ }
555
+ function isValidRewrites(value, journal) {
556
+ try {
557
+ return journal.status !== "noop" && isValidRewritePolicy(journal.rewritePolicy) && Array.isArray(value) && value.every((rewrite) => hasClosedFields(rewrite, REWRITE_FIELDS, ["kind", "description"]) && REWRITE_KINDS.has(rewrite.kind) && (rewrite.kind === "redeclaration" ? rewrite.description === FUNCTION_REDECLARATION || rewrite.description === CLASS_REDECLARATION ? journal.version === 4 && isValidBindingPolicy(journal.bindingPolicy) && journal.bindingPolicy.functionClassRedeclarations === true : journal.rewritePolicy.autoSplitRedeclarations === true : journal.rewritePolicy[REWRITE_POLICY_BY_KIND[rewrite.kind]] === true) && typeof rewrite.description === "string" && rewrite.description.length > 0 && (rewrite.source === void 0 || typeof rewrite.source === "string"));
558
+ } catch {
559
+ return false;
560
+ }
561
+ }
562
+ function isValidEditTarget(value) {
563
+ try {
564
+ return hasExactFields(value, EDIT_TARGET_FIELDS) && isSafeSequence(value.targetCallSeq);
565
+ } catch {
566
+ return false;
567
+ }
568
+ }
569
+ function isValidDerivedRun(value) {
570
+ try {
571
+ return hasExactFields(value, DERIVED_RUN_FIELDS) && typeof value.code === "string" && typeof value.description === "string";
572
+ } catch {
573
+ return false;
574
+ }
575
+ }
576
+ function isValidRecoveryBoundaries(value) {
577
+ try {
578
+ return Array.isArray(value) && value.every((boundary) => hasExactFields(boundary, RECOVERY_BOUNDARY_FIELDS) && isSafeSequence(boundary.failedCallSeq) && (boundary.frontierCallSeq === null || isSafeSequence(boundary.frontierCallSeq)));
579
+ } catch {
580
+ return false;
581
+ }
582
+ }
583
+ function isValidEditRelation(meta, args, journal) {
584
+ try {
585
+ if (!isRecord(meta) || !isRecord(args) || !isValidEditTarget(meta.dshPtcPlusEdit) || !isValidDerivedRun(meta.dshPtcPlusDerivedRun) || journal.status === "noop") return false;
586
+ if (Object.hasOwn(meta, "dshPtcPlusRecoveryBoundaries") && !isValidRecoveryBoundaries(meta.dshPtcPlusRecoveryBoundaries)) return false;
587
+ return !Object.hasOwn(args, "expected_target_call_seq") || args.expected_target_call_seq === meta.dshPtcPlusEdit.targetCallSeq;
588
+ } catch {
589
+ return false;
590
+ }
591
+ }
592
+ function rawArguments(block, settled) {
593
+ const call = settled ? block.call : block;
594
+ return typeof call?.argsRaw === "string" ? call.argsRaw : typeof call?.arguments === "string" ? call.arguments : "";
595
+ }
596
+ function parseArguments(raw) {
597
+ try {
598
+ const value = JSON.parse(raw);
599
+ return isRecord(value) ? value : void 0;
600
+ } catch {
601
+ return void 0;
602
+ }
603
+ }
604
+ function resultText(block) {
605
+ if (!Array.isArray(block.content)) return "";
606
+ try {
607
+ const parts = block.content.map((item) => item?.type === "text" && typeof item.text === "string" ? item.text : JSON.stringify(item, null, 2));
608
+ if (parts.length === 0 && isRecord(block.error)) {
609
+ return [block.error.name, block.error.code].filter((value) => typeof value === "string").join(": ");
610
+ }
611
+ return parts.filter((value) => typeof value === "string").join("\n");
612
+ } catch {
613
+ return "";
614
+ }
615
+ }
616
+ var GENERATED_RUN_CODE_DESCRIPTION_KEY = "dshPtcPlusRunCodeDescription";
617
+ function rewriteFeature(rewrites, predicate, key) {
618
+ const matching = rewrites.filter(predicate);
619
+ if (matching.length === 0) return void 0;
620
+ const sources = [...new Set(matching.map((rewrite) => rewrite.source).filter((source) => source !== void 0))];
621
+ return { key, detail: sources.join(", ") };
622
+ }
623
+ function operationFeatures(operations) {
624
+ const keys = {
625
+ save: "feature.stateSaved",
626
+ restore: "feature.stateRestored",
627
+ delete: "feature.stateDeleted"
628
+ };
629
+ return operations.map((operation) => ({
630
+ key: keys[operation.action],
631
+ detail: operation.name ?? ""
632
+ }));
633
+ }
634
+ function uniqueFeatures(features) {
635
+ const seen = /* @__PURE__ */ new Set();
636
+ return features.filter((feature) => {
637
+ if (feature === void 0) return false;
638
+ const identity = `${feature.key}\0${feature.detail}`;
639
+ if (seen.has(identity)) return false;
640
+ seen.add(identity);
641
+ return true;
642
+ });
643
+ }
644
+ function isDurableRecoveryDiagnostic(diagnostic) {
645
+ return diagnostic.code === "PTC-R002" && diagnostic.severity === "warning" && diagnostic.phase === "recover" && diagnostic.stateEffect === "rolled-back";
646
+ }
647
+ function derivePtcToolView(block, toolName = void 0) {
648
+ const settled = isRecord(block) && block.kind === "tool-result";
649
+ const argsRaw = isRecord(block) ? rawArguments(block, settled) : "";
650
+ const args = parseArguments(argsRaw);
651
+ const code = typeof args?.code === "string" ? args.code : argsRaw;
652
+ const explicitDescription = typeof args?.description === "string" && args.description.length > 0 ? args.description : void 0;
653
+ const generatedDescription = settled && isRecord(block.meta) && typeof block.meta[GENERATED_RUN_CODE_DESCRIPTION_KEY] === "string" && block.meta[GENERATED_RUN_CODE_DESCRIPTION_KEY].length > 0 ? block.meta[GENERATED_RUN_CODE_DESCRIPTION_KEY] : void 0;
654
+ const description = (explicitDescription ?? generatedDescription)?.split(/\r?\n/, 1)[0] ?? "";
655
+ const output = settled ? resultText(block) : "";
656
+ const state = !settled ? "running" : block.error?.code === "interrupted" ? "stopped" : block.isError === true ? "error" : "ok";
657
+ const journal = settled && isRecord(block.meta) && isCountableJournal(block.meta.dshPtcPlus) ? block.meta.dshPtcPlus : void 0;
658
+ if (journal === void 0) {
659
+ return Object.freeze({ state, description, code, output, ptc: false, features: Object.freeze([]) });
660
+ }
661
+ const rewrites = isValidRewrites(block.meta.dshPtcPlusRewrites, journal) ? block.meta.dshPtcPlusRewrites : [];
662
+ const resolvedToolName = typeof toolName === "string" ? toolName : typeof block.call?.name === "string" ? block.call.name : "";
663
+ const recordedToolName = typeof block.call?.name === "string" ? block.call.name : void 0;
664
+ const safeEdit = resolvedToolName === "edit_run_code" && (recordedToolName === void 0 || recordedToolName === "edit_run_code") && isValidEditRelation(block.meta, args, journal);
665
+ const features = uniqueFeatures([
666
+ safeEdit ? { key: "feature.safeEdit", detail: "" } : void 0,
667
+ rewriteFeature(rewrites, (rewrite) => rewrite.kind === "import", "autoRewriteImports.label"),
668
+ rewriteFeature(rewrites, (rewrite) => rewrite.kind === "export", "autoStripExports.label"),
669
+ rewriteFeature(rewrites, (rewrite) => rewrite.kind === "redeclaration" && rewrite.description === MIXED_REDECLARATION, "autoSplitRedeclarations.label"),
670
+ journal.calls.some((call) => call.global === "code" && call.member === "run" && call.ok === true) ? { key: "feature.codeRun", detail: "" } : void 0,
671
+ journal.diagnostics.some(isDurableRecoveryDiagnostic) ? { key: "durableReplay.label", detail: "" } : void 0,
672
+ ...operationFeatures(journal.operations)
673
+ ]);
674
+ return Object.freeze({ state, description, code, output, ptc: true, features: Object.freeze(features) });
675
+ }
676
+
677
+ // internal/record-utils.js
678
+ function isRecord2(value) {
679
+ return value !== null && typeof value === "object" && !Array.isArray(value);
680
+ }
681
+
682
+ // internal/repl-memory-projection.js
683
+ var BINDING_KINDS = /* @__PURE__ */ new Set(["variable", "function", "class", "import"]);
684
+ var MAX_BINDINGS = 128;
685
+ var MAX_BINDING_NAME_LENGTH = 128;
686
+ var MAX_DEFINITION_SOURCE_LENGTH = 1024;
687
+ var MAX_DEFINITION_SOURCE_TOTAL_LENGTH = 16 * 1024;
688
+ var SNAPSHOT_FIELDS = /* @__PURE__ */ new Set(["available", "entries", "total", "omitted"]);
689
+ var ENTRY_FIELDS = /* @__PURE__ */ new Set(["name", "kind", "definition"]);
690
+ var DEFINITION_FIELDS = /* @__PURE__ */ new Set(["source", "line", "column"]);
691
+ function exactFields(value, fields) {
692
+ if (!isRecord2(value)) return false;
693
+ const keys = Reflect.ownKeys(value);
694
+ return keys.length === fields.size && keys.every((key) => typeof key === "string" && fields.has(key) && Object.prototype.propertyIsEnumerable.call(value, key));
695
+ }
696
+ var EMPTY_REPL_MEMORY = Object.freeze({
697
+ available: false,
698
+ entries: Object.freeze([]),
699
+ total: 0,
700
+ omitted: 0
701
+ });
702
+ function unavailableReplMemorySnapshot() {
703
+ return EMPTY_REPL_MEMORY;
704
+ }
705
+ function normalizeDefinition(value) {
706
+ if (!exactFields(value, DEFINITION_FIELDS) || typeof value.source !== "string" || value.source.length === 0 || value.source.length > MAX_DEFINITION_SOURCE_LENGTH || !Number.isSafeInteger(value.line) || value.line < 1 || !Number.isSafeInteger(value.column) || value.column < 1) {
707
+ throw new Error("invalid dsh-ptc-plus REPL binding definition");
708
+ }
709
+ return Object.freeze({ source: value.source, line: value.line, column: value.column });
710
+ }
711
+ function normalizeBinding(value) {
712
+ if (!exactFields(value, ENTRY_FIELDS) || typeof value.name !== "string" || value.name.length === 0 || value.name.length > MAX_BINDING_NAME_LENGTH || !BINDING_KINDS.has(value.kind)) {
713
+ throw new Error("invalid dsh-ptc-plus REPL memory binding");
714
+ }
715
+ return Object.freeze({
716
+ name: value.name,
717
+ kind: value.kind,
718
+ definition: normalizeDefinition(value.definition)
719
+ });
720
+ }
721
+ function normalizeReplMemorySnapshot(value) {
722
+ if (!exactFields(value, SNAPSHOT_FIELDS) || typeof value.available !== "boolean" || !Array.isArray(value.entries) || value.entries.length > MAX_BINDINGS || !Number.isSafeInteger(value.total) || value.total < 0 || !Number.isSafeInteger(value.omitted) || value.omitted < 0 || value.total !== value.entries.length + value.omitted) {
723
+ throw new Error("invalid dsh-ptc-plus REPL memory snapshot");
724
+ }
725
+ if (!value.available && (value.total !== 0 || value.entries.length !== 0)) {
726
+ throw new Error("unavailable dsh-ptc-plus REPL memory snapshot must be empty");
727
+ }
728
+ const names = /* @__PURE__ */ new Set();
729
+ let sourceLength = 0;
730
+ const entries = value.entries.map((entry) => {
731
+ const normalized = normalizeBinding(entry);
732
+ if (names.has(normalized.name)) {
733
+ throw new Error("invalid dsh-ptc-plus REPL memory binding");
734
+ }
735
+ sourceLength += normalized.definition.source.length;
736
+ if (sourceLength > MAX_DEFINITION_SOURCE_TOTAL_LENGTH) {
737
+ throw new Error("dsh-ptc-plus REPL binding definitions exceed the presentation budget");
738
+ }
739
+ names.add(normalized.name);
740
+ return normalized;
741
+ });
742
+ return Object.freeze({
743
+ available: value.available,
744
+ entries: Object.freeze(entries),
745
+ total: value.total,
746
+ omitted: value.omitted
747
+ });
748
+ }
749
+
213
750
  // src/client.js
214
751
  var CLIENT_STYLE_ID = "ptc-plus-client-style";
215
752
  var CLIENT_CSS = `
@@ -218,10 +755,24 @@
218
755
  .ptcPlusHeader:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}.ptcPlusHeader:focus-visible,.ptcPlusButton:focus-visible,.ptcPlusInput:focus-visible{outline:2px solid var(--dsw-alias-interactive-primary,#4d6bfe);outline-offset:-2px}
219
756
  .ptcPlusHeadText{display:flex;flex:1;min-width:0;flex-direction:column;align-items:flex-start;gap:1px}.ptcPlusName{font-size:14px;font-weight:600;line-height:20px}.ptcPlusDescription{color:var(--dsw-alias-label-tertiary,#74777d);font-size:12px;line-height:18px;overflow-wrap:anywhere}.ptcPlusStatus{display:inline-flex;align-items:center;flex:none;padding:2px 8px;border-radius:999px;font-size:11px;font-weight:500;line-height:16px}.ptcPlusStatus[data-enabled=true]{color:var(--dsw-alias-state-success-primary,#16794f);background:var(--dsw-alias-state-success-tertiary,#e7f7ef)}.ptcPlusStatus[data-enabled=false]{color:var(--dsw-alias-label-tertiary,#74777d);background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}
220
757
  .ptcPlusChevron{display:flex;color:var(--dsw-alias-label-tertiary,#74777d);transition:transform .18s ease}.ptcPlusChevron[data-open=true]{transform:rotate(180deg)}.ptcPlusBody{display:grid;grid-template-rows:0fr;transition:grid-template-rows .2s ease}.ptcPlusBody[data-open=true]{grid-template-rows:1fr}.ptcPlusBodyInner{min-height:0;overflow:hidden}.ptcPlusFields{margin:0 16px;padding:8px 0 12px;border-top:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1))}
221
- .ptcPlusRow{display:flex;align-items:center;gap:12px;min-height:48px;border-top:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1))}.ptcPlusRow:first-child{border-top:0}.ptcPlusMain{flex:1;min-width:0}.ptcPlusLabel{font-size:14px;font-weight:500;line-height:20px}.ptcPlusDetail,.ptcPlusMessage{color:var(--dsw-alias-label-tertiary,#74777d);font-size:12px;line-height:18px;overflow-wrap:anywhere}.ptcPlusInput{box-sizing:border-box;min-width:72px;width:140px;padding:5px 8px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));border-radius:6px;background:var(--dsw-alias-bg-layer-1,#fff);color:inherit;font:12px/18px ui-monospace,SFMono-Regular,Consolas,monospace}.ptcPlusCheck{width:18px;height:18px;accent-color:var(--dsw-alias-interactive-primary,#4d6bfe)}
222
- .ptcPlusFooter{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:8px}.ptcPlusButton{min-height:32px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));border-radius:6px;background:transparent;color:inherit;cursor:pointer;font:500 13px/20px inherit;transition:background-color .16s ease,border-color .16s ease}.ptcPlusButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}.ptcPlusButton:disabled,.ptcPlusInput:disabled,.ptcPlusCheck:disabled{cursor:not-allowed;opacity:.55}.ptcPlusActive{display:inline-flex;align-items:center;gap:5px;color:var(--dsw-alias-state-success-primary,#16794f);font-size:12px;line-height:18px;white-space:nowrap}
223
- @media(max-width:560px){.ptcPlusHeader{padding:12px}.ptcPlusFields{margin:0 12px}.ptcPlusRow{align-items:flex-start;flex-direction:column;gap:6px;padding:10px 0}.ptcPlusInput{width:100%}.ptcPlusFooter{align-items:stretch;flex-direction:column}.ptcPlusButton{width:100%}}
224
- @media(prefers-reduced-motion:reduce){.ptcPlusHeader,.ptcPlusChevron,.ptcPlusBody,.ptcPlusButton{transition:none}}
758
+ .ptcPlusGroup+.ptcPlusGroup{margin-top:14px}.ptcPlusGroupTitle{margin:0;padding:8px 0 5px;color:var(--dsw-alias-label-secondary,#52565d);font-size:11px;font-weight:600;letter-spacing:0;line-height:16px}.ptcPlusRow{display:flex;align-items:center;gap:12px;min-height:48px;border-top:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1))}.ptcPlusRow:first-child{border-top:0}.ptcPlusMain{flex:1;min-width:0}.ptcPlusLabel{font-size:14px;font-weight:500;line-height:20px}.ptcPlusDetail,.ptcPlusMessage{color:var(--dsw-alias-label-tertiary,#74777d);font-size:12px;line-height:18px;overflow-wrap:anywhere}.ptcPlusInput{box-sizing:border-box;min-width:72px;width:140px;padding:5px 8px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));border-radius:6px;background:var(--dsw-alias-bg-layer-1,#fff);color:inherit;font:12px/18px ui-monospace,SFMono-Regular,Consolas,monospace}.ptcPlusCheck{width:18px;height:18px;accent-color:var(--dsw-alias-interactive-primary,#4d6bfe)}
759
+ .ptcPlusFooter{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:8px}.ptcPlusButton{min-height:32px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));border-radius:6px;background:transparent;color:inherit;cursor:pointer;font:500 13px/20px inherit;transition:background-color .16s ease,border-color .16s ease}.ptcPlusButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}.ptcPlusButton:disabled,.ptcPlusInput:disabled,.ptcPlusCheck:disabled{cursor:not-allowed;opacity:.55}
760
+ .ptcPlusActiveShell{display:inline-flex;align-items:center}.ptcPlusActive{appearance:none;display:inline-flex;height:24px;align-items:center;gap:5px;padding:0 8px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-success-primary,#16794f) 32%,transparent);border-radius:6px;background:var(--dsw-alias-state-success-tertiary,#e7f7ef);color:var(--dsw-alias-state-success-primary,#16794f);cursor:help;font:600 12px/18px inherit;white-space:nowrap;transition:background-color .14s ease,border-color .14s ease}.ptcPlusActive:hover,.ptcPlusActive[aria-expanded=true]{border-color:color-mix(in srgb,var(--dsw-alias-state-success-primary,#16794f) 48%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#16794f) 16%,var(--dsw-alias-bg-layer-3,#fff))}.ptcPlusActive:focus-visible{outline:2px solid var(--dsw-alias-state-success-primary,#16794f);outline-offset:2px}.ptcPlusReplPopover{position:fixed;z-index:2147483000;inset:auto;display:none;box-sizing:border-box;margin:0;padding:0;border:0;overflow:visible;background:transparent;color:var(--dsw-alias-label-primary,#18191c)}.ptcPlusReplPopover:popover-open,.ptcPlusReplPopover[data-open=true]{display:block}.ptcPlusReplPopover::backdrop{background:transparent}.ptcPlusReplCard{display:flex;max-height:inherit;overflow:hidden;flex-direction:column;border:1px solid color-mix(in srgb,var(--dsw-alias-state-success-primary,#16794f) 22%,var(--dsw-alias-border-l2,rgba(0,0,0,.1)));border-top:3px solid var(--dsw-alias-state-success-primary,#16794f);border-radius:8px;background:var(--dsw-alias-bg-layer-3,#fff);box-shadow:0 14px 36px rgba(16,24,40,.2),0 3px 10px rgba(16,24,40,.1);color:var(--dsw-alias-label-primary,#18191c);white-space:normal}.ptcPlusReplHead{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;column-gap:8px;padding:11px 13px 10px;border-bottom:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#16794f) 7%,var(--dsw-alias-bg-layer-3,#fff))}.ptcPlusReplStatusDot{grid-row:1/3;width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-state-success-primary,#16794f);box-shadow:0 0 0 3px color-mix(in srgb,var(--dsw-alias-state-success-primary,#16794f) 14%,transparent)}.ptcPlusReplTitle,.ptcPlusReplSummary{display:block;min-width:0}.ptcPlusReplTitle{font-size:13px;font-weight:600;line-height:19px}.ptcPlusReplSummary{color:var(--dsw-alias-label-tertiary,#74777d);font-size:11px;line-height:16px}.ptcPlusReplList{min-height:0;margin:0;padding:5px 0;overflow:auto;overscroll-behavior:contain;list-style:none;scrollbar-gutter:stable}.ptcPlusReplBinding{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:4px 10px;padding:7px 12px}.ptcPlusReplBinding:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}.ptcPlusReplIdentity{display:flex;min-width:0;align-items:center;gap:7px}.ptcPlusReplName{min-width:0;overflow:hidden;font:12px/18px ui-monospace,SFMono-Regular,Consolas,monospace;text-overflow:ellipsis;white-space:nowrap}.ptcPlusReplKind{flex:none;padding:1px 6px;border:1px solid color-mix(in srgb,currentColor 22%,transparent);border-radius:999px;background:color-mix(in srgb,currentColor 10%,transparent);font-size:10px;font-weight:600;line-height:15px}.ptcPlusReplKind[data-kind=variable]{color:var(--dsw-alias-interactive-primary,#315fbd)}.ptcPlusReplKind[data-kind=function]{color:#7651b5}.ptcPlusReplKind[data-kind=class]{color:var(--dsw-alias-state-warning-primary,#946200)}.ptcPlusReplKind[data-kind=import]{color:#14766f}.ptcPlusReplPreview{grid-column:1;min-width:0;overflow:hidden;color:var(--dsw-alias-label-tertiary,#74777d);font:11px/16px ui-monospace,SFMono-Regular,Consolas,monospace;text-overflow:ellipsis;white-space:nowrap}.ptcPlusReplInspect{grid-column:2;grid-row:1/3;display:inline-flex;align-items:center;gap:4px;padding:3px 5px;border:0;border-radius:4px;background:transparent;color:var(--dsw-alias-label-secondary,#52565d);cursor:pointer;font:500 11px/17px inherit;white-space:nowrap}.ptcPlusReplInspect:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06));color:var(--dsw-alias-interactive-primary,#4d6bfe)}.ptcPlusReplInspect:focus-visible{outline:2px solid var(--dsw-alias-interactive-primary,#4d6bfe);outline-offset:1px}.ptcPlusReplDefinition{grid-column:1/-1;min-width:0;margin-top:4px;padding:8px;border-left:2px solid var(--dsw-alias-interactive-primary,#4d6bfe);background:var(--dsw-alias-bg-layer-2,rgba(38,49,72,.03))}.ptcPlusReplLocation{display:block;margin-bottom:5px;color:var(--dsw-alias-label-tertiary,#74777d);font-size:10px;line-height:15px}.ptcPlusReplCode{max-height:180px;margin:0;overflow:auto;color:inherit;font:11px/16px ui-monospace,SFMono-Regular,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere}.ptcPlusReplEmpty,.ptcPlusReplMore{display:block;color:var(--dsw-alias-label-tertiary,#74777d)}.ptcPlusReplEmpty{padding:18px 13px;font-size:12px;line-height:18px}.ptcPlusReplMore{padding:8px 13px;border-top:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));background:var(--dsw-alias-bg-layer-2,rgba(38,49,72,.03));font-size:11px;line-height:17px}
761
+ .ptcPlusReplList{max-height:min(52vh,480px)}.ptcPlusReplBinding{grid-template-columns:minmax(0,1fr) 24px;gap:3px 8px;min-height:36px;padding:5px 12px;content-visibility:auto;contain-intrinsic-size:36px;cursor:pointer;transition:background-color .16s ease}.ptcPlusReplBinding:focus-visible{outline:2px solid var(--dsw-alias-interactive-primary,#4d6bfe);outline-offset:-2px}.ptcPlusReplBinding[data-expanded=true]{background:color-mix(in srgb,var(--dsw-alias-interactive-primary,#4d6bfe) 5%,transparent)}.ptcPlusReplName{grid-column:1}.ptcPlusReplName[data-kind=variable]{color:var(--dsw-alias-interactive-primary,#315fbd)}.ptcPlusReplName[data-kind=function]{color:#7651b5}.ptcPlusReplName[data-kind=class]{color:var(--dsw-alias-state-warning-primary,#946200)}.ptcPlusReplName[data-kind=import]{color:#14766f}.ptcPlusReplPreview{grid-column:1}.ptcPlusReplChevron{grid-column:2;grid-row:1/3;display:flex;align-items:center;justify-content:center;color:var(--dsw-alias-label-tertiary,#74777d);transition:transform .2s ease}.ptcPlusReplChevron[data-open=true]{transform:rotate(180deg)}.ptcPlusReplDefinitionWrap{grid-column:1/-1;display:grid;grid-template-rows:0fr;min-width:0;transition:grid-template-rows .24s cubic-bezier(.2,.7,.2,1)}.ptcPlusReplDefinitionWrap[data-open=true]{grid-template-rows:1fr}.ptcPlusReplDefinitionInner{min-height:0;overflow:hidden}
762
+ .ptcPlusTool{display:flex;min-width:0;flex-direction:column}.ptcPlusToolPreview{display:flex;min-width:0;flex:1 1 auto;flex-direction:row;align-items:center;overflow:hidden;margin-left:7px}.ptcPlusToolPreview .ptcPlusFeatures{flex:0 1 auto;flex-wrap:nowrap;overflow:hidden;margin:0 0 0 7px}.ptcPlusToolSummaryLine{box-sizing:border-box;display:flex;min-width:0;min-height:20px;flex:1 1 auto;align-items:center;gap:7px;padding:0;color:inherit;line-height:20px}.ptcPlusToolSummary{box-sizing:border-box;display:flex;min-width:0;min-height:32px;align-items:center;gap:7px;padding:0;color:inherit;line-height:20px}.ptcPlusToolSummary[data-expandable=true]{cursor:pointer}.ptcPlusToolSummary[data-expandable=true]:hover .ptcPlusToolTitle{color:var(--dsw-alias-interactive-primary,#4d6bfe)}.ptcPlusToolSummary:focus-visible{outline:2px solid var(--dsw-alias-interactive-primary,#4d6bfe);outline-offset:2px}.ptcPlusToolLeading{display:flex;width:16px;height:20px;flex:none;align-items:center;justify-content:center;color:var(--dsw-alias-label-tertiary,#74777d)}.ptcPlusToolChevron{transition:transform .16s ease}.ptcPlusToolChevron[data-open=true]{transform:rotate(180deg)}.ptcPlusToolTitle{display:flex;height:20px;flex:none;align-items:center;font-size:13px;font-weight:500;line-height:20px}.ptcPlusToolState{display:flex;height:20px;flex:none;align-items:center;color:var(--dsw-alias-label-tertiary,#74777d);font-size:11px;line-height:20px}.ptcPlusToolSummaryLine[data-state=running] .ptcPlusToolState,.ptcPlusToolSummary[data-state=running] .ptcPlusToolState{color:var(--dsw-alias-interactive-primary,#4d6bfe)}.ptcPlusToolSummaryLine[data-state=error] .ptcPlusToolState,.ptcPlusToolSummary[data-state=error] .ptcPlusToolState{color:var(--dsw-alias-state-danger-primary,#c43d3d)}.ptcPlusToolSummaryLine[data-state=stopped] .ptcPlusToolState,.ptcPlusToolSummary[data-state=stopped] .ptcPlusToolState{color:var(--dsw-alias-state-warning-primary,#a15c00)}.ptcPlusToolSep{width:3px;height:3px;flex:none;border-radius:50%;background:var(--dsw-alias-label-tertiary,#74777d)}.ptcPlusToolDescription{display:flex;min-width:0;min-height:20px;flex:1 1 auto;align-items:center;overflow:hidden;color:var(--dsw-alias-label-secondary,#52565d);font-size:13px;line-height:20px;text-overflow:ellipsis;white-space:nowrap}.ptcPlusToolPreview .ptcPlusFeature{flex:none;max-width:180px;white-space:nowrap}.ptcPlusToolPreview .ptcPlusFeatureDetail{max-width:120px}.ptcPlusToolSummaryLine[data-state=error] .ptcPlusToolDescription,.ptcPlusToolSummary[data-state=error] .ptcPlusToolDescription{color:var(--dsw-alias-state-danger-primary,#c43d3d)}.ptcPlusToolSummaryLine[data-state=stopped] .ptcPlusToolDescription,.ptcPlusToolSummary[data-state=stopped] .ptcPlusToolDescription{color:var(--dsw-alias-state-warning-primary,#a15c00)}
763
+ .ptcPlusFeatures{display:flex;min-width:0;flex-wrap:wrap;gap:3px 14px;margin:0 0 5px 23px}.ptcPlusFeature{display:inline-flex;min-width:0;align-items:center;gap:5px;color:var(--dsw-alias-label-secondary,#52565d);font-size:11px;line-height:17px}.ptcPlusFeature::before{width:4px;height:4px;flex:none;border-radius:50%;background:var(--dsw-alias-interactive-primary,#4d6bfe);content:''}.ptcPlusFeatureName{font-weight:500}.ptcPlusFeatureDetail{min-width:0;overflow:hidden;color:var(--dsw-alias-label-tertiary,#74777d);font-family:ui-monospace,SFMono-Regular,Consolas,monospace;text-overflow:ellipsis;white-space:nowrap}
764
+ .ptcPlusToolBody{margin:4px 0 8px 23px;border-left:2px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));background:var(--dsw-alias-bg-layer-2,rgba(38,49,72,.03))}.ptcPlusToolSection{display:flex;min-width:0;flex-direction:column;gap:4px;padding:9px 11px}.ptcPlusToolSection+.ptcPlusToolSection{border-top:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1))}.ptcPlusToolSectionLabel{color:var(--dsw-alias-label-tertiary,#74777d);font-size:10px;font-weight:600;line-height:16px;text-transform:uppercase}.ptcPlusToolCode{max-height:320px;margin:0;overflow:auto;color:inherit;font:12px/18px ui-monospace,SFMono-Regular,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere}.ptcPlusIoCard{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.1));border-radius:12px;background:var(--dsw-alias-markdown-code-block,rgba(38,49,72,.06));overflow:hidden}.ptcPlusIoText{max-height:320px;margin:0;padding:12px 16px;overflow:auto;color:var(--dsw-alias-label-secondary,#52565d);font:12px/18px ui-monospace,SFMono-Regular,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere}.ptcPlusIoText[data-error]{color:var(--dsw-alias-state-error-primary,#c43d3d)}.ptcPlusInspect{display:inline-flex;align-self:flex-start;align-items:center;gap:4px;margin:4px 0 2px 4px;padding:2px 8px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));border-radius:999px;background:var(--dsw-alias-bg-base,#fff);color:var(--dsw-alias-label-secondary,#52565d);cursor:pointer;opacity:0;font-size:11px;line-height:16px;transition:opacity .1s ease;display:inline-flex}.ptcPlusTool:hover .ptcPlusInspect,.ptcPlusInspect:focus-visible{opacity:1}.ptcPlusInspect:hover{background:var(--dsw-alias-interactive-bg-hover-solid,rgba(38,49,72,.06));color:var(--dsw-alias-label-primary,#18191c)}
765
+ @media(max-width:560px){.ptcPlusHeader{padding:12px}.ptcPlusFields{margin:0 12px}.ptcPlusRow{align-items:flex-start;flex-direction:column;gap:6px;padding:10px 0}.ptcPlusInput{width:100%}.ptcPlusFooter{align-items:stretch;flex-direction:column}.ptcPlusButton{width:100%}.ptcPlusFeatures,.ptcPlusToolBody{margin-left:0}.ptcPlusToolSummary .ptcPlusToolDescription{white-space:normal;overflow-wrap:anywhere}}
766
+ @media(prefers-reduced-motion:reduce){.ptcPlusHeader,.ptcPlusChevron,.ptcPlusBody,.ptcPlusButton,.ptcPlusActive,.ptcPlusToolChevron,.ptcPlusReplChevron,.ptcPlusReplDefinitionWrap,.ptcPlusInspect{transition:none}}
767
+ /* The summary button owns disclosure; definition content is a separate grid item. */
768
+ .ptcPlusReplBinding{padding:0;cursor:default}.ptcPlusReplBindingTrigger{appearance:none;display:grid;width:100%;grid-column:1/-1;grid-template-columns:minmax(0,1fr) 24px;gap:3px 8px;min-height:36px;padding:5px 12px;border:0;background:transparent;color:inherit;text-align:left;cursor:pointer;font:inherit;transition:background-color .16s ease}.ptcPlusReplBindingTrigger:hover,.ptcPlusReplBindingTrigger[aria-expanded=true]{background:color-mix(in srgb,var(--dsw-alias-interactive-primary,#4d6bfe) 5%,transparent)}.ptcPlusReplBindingTrigger:focus-visible{outline:2px solid var(--dsw-alias-interactive-primary,#4d6bfe);outline-offset:-2px}
769
+ /* High-contrast TypeScript-like token colors adapt to the active text theme. */
770
+ .ptcPlusReplCard .ptcPlusReplName[data-kind=variable]{color:color-mix(in srgb,#005cc5 78%,var(--dsw-alias-label-primary,#18191c))}
771
+ .ptcPlusReplCard .ptcPlusReplName[data-kind=function]{color:color-mix(in srgb,#795e26 78%,var(--dsw-alias-label-primary,#18191c))}
772
+ .ptcPlusReplCard .ptcPlusReplName[data-kind=class]{color:color-mix(in srgb,#267f99 78%,var(--dsw-alias-label-primary,#18191c))}
773
+ .ptcPlusReplCard .ptcPlusReplName[data-kind=import]{color:color-mix(in srgb,#af00db 78%,var(--dsw-alias-label-primary,#18191c))}
774
+ /* Keep the session-header action on the same compact 32px rhythm as DSH chrome. */
775
+ .ptcPlusActiveShell{display:inline-flex;height:28px;align-items:center;justify-content:center;line-height:0;vertical-align:middle}.ptcPlusActive{box-sizing:border-box;height:28px;justify-content:center;gap:6px;padding:0 6px;border:0;background:transparent;font-family:inherit;font-size:13px;font-weight:500;line-height:18px}.ptcPlusActive::before{width:6px;height:6px;flex:none;border-radius:50%;background:currentColor;box-shadow:0 0 0 2px color-mix(in srgb,currentColor 18%,transparent);content:''}.ptcPlusActive:hover,.ptcPlusActive[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}.ptcPlusActiveLabel{display:inline-flex;height:18px;align-items:center;line-height:18px}
225
776
  `;
226
777
  var LOCALE_NS = "settings.ptcPlus";
227
778
  var CHROME_COPY = Object.freeze({
@@ -238,7 +789,33 @@
238
789
  "status.applied": "\u8BBE\u7F6E\u5DF2\u7ACB\u5373\u751F\u6548",
239
790
  "status.conflict": "\u8BBE\u7F6E\u672A\u751F\u6548\uFF0C\u8BF7\u68C0\u67E5\u8BBE\u7F6E\u51B2\u7A81",
240
791
  "status.failed": "\u8BBE\u7F6E\u5931\u8D25\uFF1A{error}",
241
- "indicator.title": "PTC Plus \u5DF2\u542F\u7528"
792
+ "indicator.title": "PTC Plus \u5DF2\u542F\u7528\uFF1B\u67E5\u770B\u5F53\u524D\u53EF\u590D\u7528\u7684 REPL \u7ED1\u5B9A",
793
+ "memory.title": "REPL \u53EF\u590D\u7528\u7ED1\u5B9A",
794
+ "memory.count": "{count} \u4E2A\u53EF\u590D\u7528\u7ED1\u5B9A",
795
+ "memory.empty": "\u5F53\u524D\u6CA1\u6709\u53EF\u590D\u7528\u7ED1\u5B9A",
796
+ "memory.unavailable": "\u5F53\u524D\u7ED1\u5B9A\u72B6\u6001\u5C1A\u4E0D\u53EF\u786E\u8BA4",
797
+ "memory.more": "\u53E6\u6709 {count} \u4E2A\u7ED1\u5B9A\u672A\u663E\u793A",
798
+ "memory.kind.variable": "\u53D8\u91CF",
799
+ "memory.kind.function": "\u51FD\u6570",
800
+ "memory.kind.class": "\u7C7B",
801
+ "memory.kind.import": "\u5BFC\u5165",
802
+ "memory.location": "\u7B2C {line} \u884C\uFF0C\u7B2C {column} \u5217",
803
+ "tool.code": "\u6267\u884C",
804
+ "tool.codeEdit": "\u4FEE\u6B63\u6267\u884C",
805
+ "tool.running": "\u6B63\u5728\u8FD0\u884C",
806
+ "tool.completed": "\u6267\u884C\u5B8C\u6210",
807
+ "tool.failed": "\u6267\u884C\u5931\u8D25",
808
+ "tool.stopped": "\u6267\u884C\u5DF2\u4E2D\u65AD",
809
+ "tool.source": "\u6E90\u7801",
810
+ "tool.result": "\u7ED3\u679C",
811
+ "tool.copy": "\u590D\u5236\u4EE3\u7801",
812
+ "tool.copied": "\u5DF2\u590D\u5236",
813
+ "tool.inspect": "\u68C0\u67E5\u8C03\u7528",
814
+ "feature.safeEdit": "\u5B89\u5168\u7F16\u8F91\u6267\u884C",
815
+ "feature.codeRun": "\u9694\u79BB\u6267\u884C code.run",
816
+ "feature.stateSaved": "\u4FDD\u5B58 REPL \u72B6\u6001",
817
+ "feature.stateRestored": "\u6062\u590D REPL \u72B6\u6001",
818
+ "feature.stateDeleted": "\u5220\u9664 REPL \u72B6\u6001"
242
819
  }),
243
820
  en: Object.freeze({
244
821
  "card.description": "The session-bound TypeScript REPL for PTC mode.",
@@ -253,7 +830,33 @@
253
830
  "status.applied": "Setting applied immediately.",
254
831
  "status.conflict": "The setting did not take effect; check for conflicting settings.",
255
832
  "status.failed": "Could not save: {error}",
256
- "indicator.title": "PTC Plus is active"
833
+ "indicator.title": "PTC Plus is active; view reusable REPL bindings",
834
+ "memory.title": "Reusable REPL bindings",
835
+ "memory.count": "{count} reusable bindings",
836
+ "memory.empty": "No reusable bindings",
837
+ "memory.unavailable": "Current binding state cannot be confirmed",
838
+ "memory.more": "{count} more bindings not shown",
839
+ "memory.kind.variable": "Variable",
840
+ "memory.kind.function": "Function",
841
+ "memory.kind.class": "Class",
842
+ "memory.kind.import": "Import",
843
+ "memory.location": "Line {line}, column {column}",
844
+ "tool.code": "Code",
845
+ "tool.codeEdit": "Code edit",
846
+ "tool.running": "Running",
847
+ "tool.completed": "Completed",
848
+ "tool.failed": "Failed",
849
+ "tool.stopped": "Stopped",
850
+ "tool.source": "Source",
851
+ "tool.result": "Result",
852
+ "tool.inspect": "Inspect call",
853
+ "feature.safeEdit": "Safe edit execution",
854
+ "feature.codeRun": "Isolated code.run execution",
855
+ "feature.stateSaved": "Saved REPL state",
856
+ "feature.stateRestored": "Restored REPL state",
857
+ "feature.stateDeleted": "Deleted REPL state",
858
+ "tool.copy": "Copy code",
859
+ "tool.copied": "Copied"
257
860
  })
258
861
  });
259
862
  function fieldCopy(field, locale) {
@@ -265,6 +868,9 @@
265
868
  var SETTINGS_COPY = Object.freeze(Object.fromEntries(
266
869
  ["zh", "en"].map((locale) => [locale, Object.freeze({
267
870
  ...CHROME_COPY[locale],
871
+ ...Object.assign({}, ...CONFIG_GROUPS.map((group) => ({
872
+ [`group.${group.key}`]: locale === "en" ? group.labelEn : group.label
873
+ }))),
268
874
  ...Object.assign({}, ...CONFIG_FIELDS.map((field) => fieldCopy(field, locale)))
269
875
  })])
270
876
  ));
@@ -278,8 +884,11 @@
278
884
  factory: (require2) => {
279
885
  const React = require2("react");
280
886
  const {
887
+ CodeBlock,
888
+ DisclosureRow,
281
889
  IconCheckOutline14,
282
- IconChevronDownOutline14
890
+ IconChevronDownOutline14,
891
+ IconInspectOutline12
283
892
  } = require2("@deepseek-ai/dsh-client-ui-primitives");
284
893
  const module = { exports: {} };
285
894
  const h = React.createElement;
@@ -369,6 +978,33 @@
369
978
  });
370
979
  };
371
980
  const fieldDisabled = (field) => unavailable || pending.has(field.key) || field.key !== "enabled" && !enabled;
981
+ const settingGroups = CONFIG_GROUPS.map((group) => h(
982
+ "section",
983
+ {
984
+ key: group.key,
985
+ className: "ptcPlusGroup",
986
+ "aria-labelledby": `ptc-plus-settings-group-${group.key}`
987
+ },
988
+ h("h3", {
989
+ id: `ptc-plus-settings-group-${group.key}`,
990
+ className: "ptcPlusGroupTitle"
991
+ }, t(`group.${group.key}`)),
992
+ ...group.fields.map((key) => {
993
+ const field = CONFIG_FIELDS.find((candidate) => candidate.key === key);
994
+ if (field === void 0) return null;
995
+ return h(
996
+ "div",
997
+ { key: field.key, className: "ptcPlusRow" },
998
+ h(
999
+ "div",
1000
+ { className: "ptcPlusMain" },
1001
+ h("div", { className: "ptcPlusLabel" }, t(`${field.key}.label`)),
1002
+ field.description === "" ? null : h("div", { className: "ptcPlusDetail" }, t(`${field.key}.description`))
1003
+ ),
1004
+ fieldInput(field, value[field.key], fieldDisabled(field), persist, t(`${field.key}.label`))
1005
+ );
1006
+ })
1007
+ ));
372
1008
  return h(
373
1009
  "li",
374
1010
  { className: "ptcPlusCard" },
@@ -398,17 +1034,7 @@
398
1034
  "div",
399
1035
  { className: "ptcPlusFields" },
400
1036
  snapshot.status === "loading" ? h("p", { className: "ptcPlusMessage" }, t("state.syncing")) : snapshot.status === "unavailable" ? h("p", { className: "ptcPlusMessage" }, t("state.unavailable")) : [
401
- ...CONFIG_FIELDS.map((field) => h(
402
- "div",
403
- { key: field.key, className: "ptcPlusRow" },
404
- h(
405
- "div",
406
- { className: "ptcPlusMain" },
407
- h("div", { className: "ptcPlusLabel" }, t(`${field.key}.label`)),
408
- field.description === "" ? null : h("div", { className: "ptcPlusDetail" }, t(`${field.key}.description`))
409
- ),
410
- fieldInput(field, value[field.key], fieldDisabled(field), persist, t(`${field.key}.label`))
411
- )),
1037
+ ...settingGroups,
412
1038
  h(
413
1039
  "div",
414
1040
  { key: "footer", className: "ptcPlusFooter" },
@@ -424,24 +1050,422 @@
424
1050
  key: SETTINGS_NAMESPACE,
425
1051
  locale: LOCALE_NS
426
1052
  }, PTCPlusSettingsCard));
1053
+ function PTCPlusToolRow({ toolName, block, inspect, t }) {
1054
+ const [open, setOpen] = React.useState(false);
1055
+ const view = derivePtcToolView(block, toolName);
1056
+ const expandable = view.code !== "" || view.output !== "" || typeof inspect === "function";
1057
+ const stateKey = {
1058
+ running: "tool.running",
1059
+ ok: "tool.completed",
1060
+ error: "tool.failed",
1061
+ stopped: "tool.stopped"
1062
+ }[view.state];
1063
+ const outputSummary = view.state === "error" && view.output !== "" ? view.output.split(/\r?\n/, 1)[0] : "";
1064
+ const summary = outputSummary || view.description;
1065
+ const stateText = view.state === "ok" ? null : t(stateKey);
1066
+ const toggle = () => {
1067
+ if (expandable) setOpen((current) => !current);
1068
+ };
1069
+ const summaryText = summary === "" ? null : summary;
1070
+ const summaryLine = stateText === null && summaryText === null ? null : h(
1071
+ "div",
1072
+ { className: "ptcPlusToolSummaryLine", "data-state": view.state, role: "status" },
1073
+ stateText === null ? null : h("span", { className: "ptcPlusToolState" }, stateText),
1074
+ summaryText === null ? null : h("span", { className: "ptcPlusToolSep", "aria-hidden": true }),
1075
+ summaryText === null ? null : h("span", { className: "ptcPlusToolDescription" }, summaryText)
1076
+ );
1077
+ const body = !open ? null : h(
1078
+ "div",
1079
+ { className: "ptcPlusToolBody" },
1080
+ view.code === "" ? null : h(
1081
+ "div",
1082
+ { className: "ptcPlusToolSection" },
1083
+ h("span", { className: "ptcPlusToolSectionLabel" }, t("tool.source")),
1084
+ typeof CodeBlock === "function" ? h(CodeBlock, {
1085
+ code: view.code,
1086
+ lang: "typescript",
1087
+ className: "ptcPlusToolCode",
1088
+ copyLabel: t("tool.copy"),
1089
+ copiedLabel: t("tool.copied")
1090
+ }) : h("pre", { className: "ptcPlusToolCode" }, view.code)
1091
+ ),
1092
+ view.output === "" ? null : h(
1093
+ "div",
1094
+ { className: "ptcPlusToolSection" },
1095
+ h("span", { className: "ptcPlusToolSectionLabel" }, t("tool.result")),
1096
+ h(
1097
+ "div",
1098
+ { className: "ptcPlusIoCard" },
1099
+ h("pre", {
1100
+ className: "ptcPlusIoText",
1101
+ "data-error": view.state === "error" || void 0
1102
+ }, view.output)
1103
+ )
1104
+ ),
1105
+ typeof inspect !== "function" ? null : h("button", {
1106
+ type: "button",
1107
+ className: "ptcPlusInspect",
1108
+ onClick: inspect
1109
+ }, h(IconInspectOutline12, { "aria-hidden": true }), t("tool.inspect"))
1110
+ );
1111
+ const features = view.features.length === 0 ? null : h(
1112
+ "div",
1113
+ { className: "ptcPlusFeatures" },
1114
+ view.features.map((feature) => h(
1115
+ "span",
1116
+ {
1117
+ key: `${feature.key}:${feature.detail}`,
1118
+ className: "ptcPlusFeature"
1119
+ },
1120
+ h("span", { className: "ptcPlusFeatureName" }, t(feature.key)),
1121
+ feature.detail === "" ? null : h("span", { className: "ptcPlusFeatureDetail", title: feature.detail }, feature.detail)
1122
+ ))
1123
+ );
1124
+ const collapsedContent = h("div", { className: "ptcPlusToolPreview" }, summaryLine, features);
1125
+ if (typeof DisclosureRow === "function") {
1126
+ return h(
1127
+ "div",
1128
+ { className: "ptcPlusTool" },
1129
+ h(DisclosureRow, {
1130
+ icon: expandable ? h(IconChevronDownOutline14, { size: 14 }) : h(IconCheckOutline14, { size: 14 }),
1131
+ title: t(toolName === "edit_run_code" ? "tool.codeEdit" : "tool.code"),
1132
+ open,
1133
+ expandable,
1134
+ onToggle: toggle,
1135
+ expandOnRowClick: true,
1136
+ previewChevron: false,
1137
+ keepContentWhenOpen: true,
1138
+ collapsedContent,
1139
+ children: body
1140
+ })
1141
+ );
1142
+ }
1143
+ return h(
1144
+ "div",
1145
+ { className: "ptcPlusTool" },
1146
+ h(
1147
+ "div",
1148
+ {
1149
+ className: "ptcPlusToolSummary",
1150
+ "data-state": view.state,
1151
+ "data-expandable": expandable || void 0,
1152
+ role: expandable ? "button" : void 0,
1153
+ tabIndex: expandable ? 0 : void 0,
1154
+ "aria-expanded": expandable ? open : void 0,
1155
+ onClick: expandable ? toggle : void 0,
1156
+ onKeyDown: expandable ? (event) => {
1157
+ if (event.key !== "Enter" && event.key !== " ") return;
1158
+ event.preventDefault();
1159
+ toggle();
1160
+ } : void 0
1161
+ },
1162
+ h("span", { className: "ptcPlusToolLeading", "aria-hidden": true }, expandable ? h(IconChevronDownOutline14, { size: 14, className: "ptcPlusToolChevron", "data-open": open }) : h(IconCheckOutline14, { size: 14 })),
1163
+ h("span", { className: "ptcPlusToolTitle" }, t(
1164
+ toolName === "edit_run_code" ? "tool.codeEdit" : "tool.code"
1165
+ )),
1166
+ view.state === "ok" ? null : h("span", { className: "ptcPlusToolState", role: "status" }, t(stateKey)),
1167
+ h("span", { className: "ptcPlusToolSep", "aria-hidden": true }),
1168
+ h("span", { className: "ptcPlusToolDescription" }, summary)
1169
+ ),
1170
+ features,
1171
+ body
1172
+ );
1173
+ }
1174
+ ctx.slots.inject("tool.call.toolview", () => {
1175
+ let releaseRows;
1176
+ const release = () => {
1177
+ releaseRows?.();
1178
+ releaseRows = void 0;
1179
+ };
1180
+ const sync = () => {
1181
+ const snapshot = preferenceScope.getSnapshot();
1182
+ const enabled = snapshot.status === "ready" && snapshot.value?.enabled === true && snapshot.value?.enhancedToolView !== false;
1183
+ if (enabled === (releaseRows !== void 0)) return;
1184
+ release();
1185
+ if (!enabled) return;
1186
+ const disposers = [];
1187
+ try {
1188
+ disposers.push(ctx.slots.register({
1189
+ name: "tool.call.toolview",
1190
+ key: "run_code",
1191
+ locale: LOCALE_NS
1192
+ }, PTCPlusToolRow));
1193
+ disposers.push(ctx.slots.register({
1194
+ name: "tool.call.toolview",
1195
+ key: "edit_run_code",
1196
+ locale: LOCALE_NS
1197
+ }, PTCPlusToolRow));
1198
+ } catch (error) {
1199
+ disposers.reverse().forEach((dispose) => dispose());
1200
+ throw error;
1201
+ }
1202
+ releaseRows = () => disposers.reverse().forEach((dispose) => dispose());
1203
+ };
1204
+ sync();
1205
+ const unsubscribe = preferenceScope.subscribe(sync);
1206
+ return () => {
1207
+ unsubscribe();
1208
+ release();
1209
+ };
1210
+ });
427
1211
  ctx.inject(["slots", "sessions"], (scope) => {
428
- function PTCPlusSessionIndicator({ sessionId, t }) {
429
- const sessions = React.useSyncExternalStore(
430
- (listener) => scope.sessions.list.subscribe(listener),
431
- () => scope.sessions.list.getSnapshot(),
432
- () => scope.sessions.list.getSnapshot()
1212
+ function replPopoverIsOpen(popover) {
1213
+ if (popover?.dataset?.open === "true") return true;
1214
+ try {
1215
+ return popover?.matches?.(":popover-open") === true;
1216
+ } catch {
1217
+ return false;
1218
+ }
1219
+ }
1220
+ function placeReplPopover(trigger, popover) {
1221
+ if (trigger === null || popover === null) return;
1222
+ const margin = 12;
1223
+ const gap = 8;
1224
+ const viewportWidth = document.documentElement.clientWidth || window.innerWidth;
1225
+ const viewportHeight = document.documentElement.clientHeight || window.innerHeight;
1226
+ const triggerRect = trigger.getBoundingClientRect();
1227
+ const width = Math.min(344, Math.max(0, viewportWidth - margin * 2));
1228
+ const left = Math.min(
1229
+ Math.max(margin, triggerRect.right - width),
1230
+ Math.max(margin, viewportWidth - width - margin)
1231
+ );
1232
+ const below = Math.max(0, viewportHeight - triggerRect.bottom - gap - margin);
1233
+ const above = Math.max(0, triggerRect.top - gap - margin);
1234
+ const opensAbove = below < 260 && above > below;
1235
+ const availableHeight = Math.max(80, opensAbove ? above : below);
1236
+ popover.style.width = `${width}px`;
1237
+ popover.style.maxHeight = `${availableHeight}px`;
1238
+ popover.style.left = `${left}px`;
1239
+ popover.style.top = opensAbove ? `${Math.max(margin, triggerRect.top - gap - Math.min(popover.offsetHeight, availableHeight))}px` : `${Math.min(viewportHeight - margin, triggerRect.bottom + gap)}px`;
1240
+ }
1241
+ function ReplMemoryCard({ memory, t, id, titleId, popoverRef, onEnter, onLeave }) {
1242
+ const [expandedBinding, setExpandedBinding] = React.useState(null);
1243
+ return h(
1244
+ "div",
1245
+ {
1246
+ className: "ptcPlusReplPopover",
1247
+ id,
1248
+ ref: popoverRef,
1249
+ popover: "auto",
1250
+ role: "dialog",
1251
+ "aria-labelledby": titleId,
1252
+ onPointerEnter: onEnter,
1253
+ onPointerLeave: onLeave
1254
+ },
1255
+ h(
1256
+ "div",
1257
+ { className: "ptcPlusReplCard" },
1258
+ h(
1259
+ "div",
1260
+ { className: "ptcPlusReplHead" },
1261
+ h("span", { className: "ptcPlusReplStatusDot", "aria-hidden": true }),
1262
+ h("span", { className: "ptcPlusReplTitle", id: titleId }, t("memory.title")),
1263
+ memory.available ? h("span", { className: "ptcPlusReplSummary" }, t("memory.count", { count: memory.total })) : null
1264
+ ),
1265
+ !memory.available ? h("span", { className: "ptcPlusReplEmpty" }, t("memory.unavailable")) : memory.entries.length === 0 ? h("span", { className: "ptcPlusReplEmpty" }, t("memory.empty")) : h("ul", { className: "ptcPlusReplList" }, memory.entries.map((binding, index) => {
1266
+ const expanded = expandedBinding === binding.name;
1267
+ const preview = binding.definition.source.replace(/\s+/g, " ").trim();
1268
+ const definitionId = `${id}-binding-${index}`;
1269
+ const toggle = () => setExpandedBinding((current) => current === binding.name ? null : binding.name);
1270
+ return h(
1271
+ "li",
1272
+ {
1273
+ className: "ptcPlusReplBinding",
1274
+ key: binding.name,
1275
+ "data-expanded": expanded
1276
+ },
1277
+ h(
1278
+ "button",
1279
+ {
1280
+ className: "ptcPlusReplBindingTrigger",
1281
+ type: "button",
1282
+ "aria-expanded": expanded,
1283
+ "aria-controls": expanded ? definitionId : void 0,
1284
+ onClick: toggle
1285
+ },
1286
+ h("span", {
1287
+ className: "ptcPlusReplName",
1288
+ "data-kind": binding.kind,
1289
+ title: `${binding.name} - ${t(`memory.kind.${binding.kind}`)}`
1290
+ }, binding.name),
1291
+ h("span", { className: "ptcPlusReplPreview", title: preview }, preview),
1292
+ h("span", {
1293
+ className: "ptcPlusReplChevron",
1294
+ "data-open": expanded,
1295
+ "aria-hidden": true
1296
+ }, h(IconChevronDownOutline14, { size: 14 }))
1297
+ ),
1298
+ expanded ? h("div", {
1299
+ className: "ptcPlusReplDefinitionWrap",
1300
+ "data-open": true,
1301
+ id: definitionId,
1302
+ role: "region",
1303
+ "aria-label": binding.name
1304
+ }, h(
1305
+ "div",
1306
+ { className: "ptcPlusReplDefinitionInner" },
1307
+ h(
1308
+ "div",
1309
+ { className: "ptcPlusReplDefinition" },
1310
+ h("span", { className: "ptcPlusReplLocation" }, t("memory.location", {
1311
+ line: binding.definition.line,
1312
+ column: binding.definition.column
1313
+ })),
1314
+ typeof CodeBlock === "function" ? h(CodeBlock, {
1315
+ code: binding.definition.source,
1316
+ lang: "typescript",
1317
+ className: "ptcPlusReplCode",
1318
+ copyLabel: t("tool.copy"),
1319
+ copiedLabel: t("tool.copied")
1320
+ }) : h("pre", { className: "ptcPlusReplCode" }, binding.definition.source)
1321
+ )
1322
+ )) : null
1323
+ );
1324
+ })),
1325
+ memory.omitted === 0 ? null : h("span", { className: "ptcPlusReplMore" }, t("memory.more", { count: memory.omitted }))
1326
+ )
433
1327
  );
1328
+ }
1329
+ function PTCPlusSessionIndicator({ sessionId, t, useSession, useProjection, useSessions }) {
1330
+ let conversation;
1331
+ try {
1332
+ conversation = typeof useSession === "function" ? useSession((snapshot) => snapshot) : void 0;
1333
+ } catch {
1334
+ conversation = void 0;
1335
+ }
1336
+ let projectionMemory;
1337
+ try {
1338
+ projectionMemory = typeof useProjection === "function" ? useProjection("ptcPlusRepl") : void 0;
1339
+ } catch {
1340
+ projectionMemory = void 0;
1341
+ }
1342
+ let sessions;
1343
+ try {
1344
+ sessions = typeof useSessions === "function" ? useSessions((snapshot) => snapshot) : React.useSyncExternalStore(
1345
+ (listener) => scope.sessions?.list?.subscribe?.(listener) ?? (() => {
1346
+ }),
1347
+ () => scope.sessions?.list?.getSnapshot?.() ?? {},
1348
+ () => scope.sessions?.list?.getSnapshot?.() ?? {}
1349
+ );
1350
+ } catch {
1351
+ sessions = {};
1352
+ }
434
1353
  const settings = React.useSyncExternalStore(
435
1354
  (listener) => preferenceScope.subscribe(listener),
436
1355
  () => preferenceScope.getSnapshot(),
437
1356
  () => preferenceScope.getSnapshot()
438
1357
  );
439
- if (!sessionUsesPtcPreset(sessions.byId?.[sessionId]) || settings.status !== "ready" || settings.value?.enabled !== true) return null;
1358
+ const triggerRef = React.useRef(null);
1359
+ const popoverRef = React.useRef(null);
1360
+ const closeTimer = React.useRef(void 0);
1361
+ const [expanded, setExpanded] = React.useState(false);
1362
+ const positionPopover = React.useCallback(() => {
1363
+ if (!replPopoverIsOpen(popoverRef.current)) return;
1364
+ placeReplPopover(triggerRef.current, popoverRef.current);
1365
+ }, []);
1366
+ const showPopover = React.useCallback(() => {
1367
+ if (closeTimer.current !== void 0) clearTimeout(closeTimer.current);
1368
+ const popover = popoverRef.current;
1369
+ if (popover === null) return;
1370
+ popover.style.visibility = "hidden";
1371
+ if (!replPopoverIsOpen(popover)) {
1372
+ if (typeof popover.showPopover === "function") {
1373
+ try {
1374
+ popover.showPopover();
1375
+ } catch {
1376
+ popover.dataset.open = "true";
1377
+ }
1378
+ } else {
1379
+ popover.dataset.open = "true";
1380
+ }
1381
+ }
1382
+ placeReplPopover(triggerRef.current, popover);
1383
+ popover.style.visibility = "visible";
1384
+ setExpanded(true);
1385
+ }, []);
1386
+ const hidePopover = React.useCallback(() => {
1387
+ const popover = popoverRef.current;
1388
+ if (popover === null) return;
1389
+ if (popover.dataset.open === "true") delete popover.dataset.open;
1390
+ if (typeof popover.hidePopover === "function" && replPopoverIsOpen(popover)) {
1391
+ try {
1392
+ popover.hidePopover();
1393
+ } catch {
1394
+ }
1395
+ }
1396
+ setExpanded(false);
1397
+ }, []);
1398
+ const scheduleHide = React.useCallback(() => {
1399
+ if (closeTimer.current !== void 0) clearTimeout(closeTimer.current);
1400
+ closeTimer.current = setTimeout(() => {
1401
+ closeTimer.current = void 0;
1402
+ if (document.activeElement === triggerRef.current || popoverRef.current?.contains(document.activeElement)) return;
1403
+ hidePopover();
1404
+ }, 120);
1405
+ }, [hidePopover]);
1406
+ React.useEffect(() => {
1407
+ const syncPopoverState = (event) => {
1408
+ if (event.target !== popoverRef.current) return;
1409
+ setExpanded(replPopoverIsOpen(popoverRef.current));
1410
+ };
1411
+ window.addEventListener("resize", positionPopover);
1412
+ document.addEventListener("scroll", positionPopover, true);
1413
+ document.addEventListener("toggle", syncPopoverState, true);
1414
+ return () => {
1415
+ if (closeTimer.current !== void 0) clearTimeout(closeTimer.current);
1416
+ window.removeEventListener("resize", positionPopover);
1417
+ document.removeEventListener("scroll", positionPopover, true);
1418
+ document.removeEventListener("toggle", syncPopoverState, true);
1419
+ const popover = popoverRef.current;
1420
+ if (popover?.dataset?.open === "true") delete popover.dataset.open;
1421
+ if (typeof popover?.hidePopover === "function" && replPopoverIsOpen(popover)) {
1422
+ try {
1423
+ popover.hidePopover();
1424
+ } catch {
1425
+ }
1426
+ }
1427
+ };
1428
+ }, [hidePopover, positionPopover]);
1429
+ const resolvedSessionId = sessionId ?? conversation?.sessionId;
1430
+ const session = sessions?.byId?.[resolvedSessionId] ?? conversation;
1431
+ if (!sessionUsesPtcPreset(session) || settings.status !== "ready" || settings.value?.enabled !== true) return null;
1432
+ let memory;
1433
+ try {
1434
+ memory = normalizeReplMemorySnapshot(projectionMemory ?? session?.projectionValues?.ptcPlusRepl);
1435
+ } catch {
1436
+ memory = unavailableReplMemorySnapshot();
1437
+ }
1438
+ const popoverId = `ptc-plus-repl-${String(resolvedSessionId).replace(/[^A-Za-z0-9_-]/g, "-")}`;
1439
+ const titleId = `${popoverId}-title`;
440
1440
  return h(
441
1441
  "span",
442
- { className: "ptcPlusActive", title: t("indicator.title") },
443
- h(IconCheckOutline14, { size: 14 }),
444
- "PTC Plus"
1442
+ { className: "ptcPlusActiveShell" },
1443
+ h("button", {
1444
+ type: "button",
1445
+ className: "ptcPlusActive",
1446
+ ref: triggerRef,
1447
+ "aria-label": t("indicator.title"),
1448
+ "aria-controls": popoverId,
1449
+ "aria-expanded": expanded,
1450
+ "aria-haspopup": "dialog",
1451
+ onPointerEnter: showPopover,
1452
+ onPointerLeave: scheduleHide,
1453
+ onFocus: showPopover,
1454
+ onBlur: scheduleHide,
1455
+ onClick: showPopover,
1456
+ onKeyDown: (event) => {
1457
+ if (event.key === "Escape") hidePopover();
1458
+ }
1459
+ }, h("span", { className: "ptcPlusActiveLabel" }, "PTC Plus")),
1460
+ h(ReplMemoryCard, {
1461
+ memory,
1462
+ t,
1463
+ id: popoverId,
1464
+ titleId,
1465
+ popoverRef,
1466
+ onEnter: showPopover,
1467
+ onLeave: scheduleHide
1468
+ })
445
1469
  );
446
1470
  }
447
1471
  scope.slots.inject("conversation.session.header.actions", () => scope.slots.register({