@fitchmultz/pi-subagents 0.50.0 → 0.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/CHANGELOG.md +51 -2
  2. package/README.md +36 -5
  3. package/dist/agent/contracts.js +6 -24
  4. package/dist/agent/host.js +3 -16
  5. package/dist/agent/index.js +207 -173
  6. package/dist/agent/journal-tail.js +115 -156
  7. package/dist/agent/launch.js +20 -8
  8. package/dist/agent/legacy.js +65 -23
  9. package/dist/agent/notifications.js +138 -47
  10. package/dist/agent/observation-state.js +246 -0
  11. package/dist/agent/observation-view.js +258 -0
  12. package/dist/agent/observation.js +346 -0
  13. package/dist/agent/observe-card.js +243 -0
  14. package/dist/agent/observe-chrome.js +85 -0
  15. package/dist/agent/observe-client.js +285 -0
  16. package/dist/agent/observe-layout.js +65 -0
  17. package/dist/agent/observe-reading.js +84 -0
  18. package/dist/agent/observe-screen.js +400 -96
  19. package/dist/agent/observe-transcript.js +382 -0
  20. package/dist/agent/observe.js +250 -43
  21. package/dist/agent/process.js +198 -25
  22. package/dist/agent/receipts.js +92 -20
  23. package/dist/agent/runner.js +125 -20
  24. package/dist/agent/shell.js +2 -1
  25. package/dist/agent/storage.js +29 -12
  26. package/dist/agent/tool-schemas.js +20 -0
  27. package/dist/agent/typebox.js +10 -5
  28. package/dist/agent/usage.js +0 -115
  29. package/dist/agent/view.js +46 -16
  30. package/dist/agent/wake.js +99 -0
  31. package/dist/build-info.js +1 -1
  32. package/dist/intercom-client.d.ts +26 -0
  33. package/dist/intercom-client.js +45 -0
  34. package/dist/pi-intercom/agent-dir.d.ts +1 -0
  35. package/dist/pi-intercom/bridge-session.js +2 -1
  36. package/dist/pi-intercom/broker/broker.js +27 -21
  37. package/dist/pi-intercom/broker/client-last.d.ts +13 -0
  38. package/dist/pi-intercom/broker/client-last.js +17 -5
  39. package/dist/pi-intercom/broker/client.d.ts +60 -0
  40. package/dist/pi-intercom/broker/client.js +47 -16
  41. package/dist/pi-intercom/broker/connection.d.ts +29 -0
  42. package/dist/pi-intercom/broker/framing.d.ts +17 -0
  43. package/dist/pi-intercom/broker/framing.js +10 -7
  44. package/dist/pi-intercom/broker/guard.js +23 -0
  45. package/dist/pi-intercom/broker/last.d.ts +26 -0
  46. package/dist/pi-intercom/broker/last.js +57 -32
  47. package/dist/pi-intercom/broker/paths.d.ts +13 -0
  48. package/dist/pi-intercom/broker/paths.js +35 -12
  49. package/dist/pi-intercom/broker/pid.js +5 -1
  50. package/dist/pi-intercom/broker/server-connection.js +19 -0
  51. package/dist/pi-intercom/broker/snapshot-reader.d.ts +9 -0
  52. package/dist/pi-intercom/broker/snapshots.d.ts +29 -0
  53. package/dist/pi-intercom/broker/targets.d.ts +9 -0
  54. package/dist/pi-intercom/connection.js +10 -2
  55. package/dist/pi-intercom/identity.js +6 -5
  56. package/dist/pi-intercom/inbound-delivery.js +16 -60
  57. package/dist/pi-intercom/inbound-journal.js +83 -6
  58. package/dist/pi-intercom/inbound-record.js +18 -1
  59. package/dist/pi-intercom/inbound.js +12 -38
  60. package/dist/pi-intercom/index.js +3 -0
  61. package/dist/pi-intercom/last-message.js +4 -16
  62. package/dist/pi-intercom/lifecycle.js +88 -3
  63. package/dist/pi-intercom/outbound.js +5 -15
  64. package/dist/pi-intercom/reply-tracker.js +7 -6
  65. package/dist/pi-intercom/reply-wait.js +2 -2
  66. package/dist/pi-intercom/session-targets.d.ts +24 -0
  67. package/dist/pi-intercom/tool-registration.js +4 -1
  68. package/dist/pi-intercom/tool-results.js +5 -7
  69. package/dist/pi-intercom/tool-schemas.js +12 -26
  70. package/dist/pi-intercom/types.d.ts +212 -0
  71. package/dist/pi-intercom/types.js +54 -3
  72. package/dist/pi-intercom/validation.d.ts +12 -0
  73. package/dist/pi-intercom/validation.js +3 -0
  74. package/dist/shared/bounded-json.js +151 -0
  75. package/dist/shared/compact-cards.js +66 -0
  76. package/dist/shared/journal-frames.js +3 -6
  77. package/dist/shared/json-projection.js +267 -129
  78. package/dist/shared/native-journal.js +123 -56
  79. package/dist/shared/post-exit-stdio-guard.js +120 -7
  80. package/dist/shared/tui-action.js +29 -0
  81. package/dist/shared/unknown.d.ts +9 -0
  82. package/docs/code-quality.md +2 -0
  83. package/docs/development.md +40 -1
  84. package/docs/intercom-bridge.md +9 -4
  85. package/docs/intercom.md +226 -6
  86. package/docs/performance.md +87 -11
  87. package/docs/reference.md +157 -20
  88. package/docs/retired-history-maintenance.md +252 -0
  89. package/package.json +28 -15
  90. package/scripts/archive-retired-history.mjs +464 -0
  91. package/scripts/build.mjs +48 -28
  92. package/scripts/measure-size.mjs +1 -1
  93. package/scripts/native-package-smoke.mjs +153 -12
  94. package/scripts/package-smoke.mjs +80 -11
  95. package/scripts/prepare.mjs +2 -2
  96. package/scripts/real-pi-live.mjs +377 -0
  97. package/scripts/real-pi-processes.mjs +6 -1
  98. package/scripts/real-pi-smoke.mjs +6 -282
  99. package/scripts/run-tests.mjs +4 -2
  100. package/scripts/typescript.mjs +35 -0
  101. package/skills/pi-intercom/SKILL.md +2 -1
  102. package/skills/pi-subagents/SKILL.md +23 -5
  103. package/src/agent/contracts.ts +8 -34
  104. package/src/agent/host.ts +19 -20
  105. package/src/agent/index.ts +224 -190
  106. package/src/agent/journal-tail.ts +141 -153
  107. package/src/agent/launch.ts +33 -9
  108. package/src/agent/legacy.ts +85 -31
  109. package/src/agent/notifications.ts +192 -67
  110. package/src/agent/observation-state.ts +277 -0
  111. package/src/agent/observation-view.ts +267 -0
  112. package/src/agent/observation.ts +405 -0
  113. package/src/agent/observe-card.ts +304 -0
  114. package/src/agent/observe-chrome.ts +170 -0
  115. package/src/agent/observe-client.ts +318 -0
  116. package/src/agent/observe-layout.ts +79 -0
  117. package/src/agent/observe-reading.ts +97 -0
  118. package/src/agent/observe-screen.ts +443 -129
  119. package/src/agent/observe-transcript.ts +434 -0
  120. package/src/agent/observe.ts +304 -71
  121. package/src/agent/process.ts +240 -34
  122. package/src/agent/receipts.ts +114 -24
  123. package/src/agent/runner.ts +125 -27
  124. package/src/agent/shell.ts +2 -1
  125. package/src/agent/storage.ts +27 -11
  126. package/src/agent/tool-schemas.ts +33 -0
  127. package/src/agent/typebox.ts +12 -7
  128. package/src/agent/usage.ts +0 -130
  129. package/src/agent/view.ts +70 -20
  130. package/src/agent/wake.ts +107 -0
  131. package/src/intercom-client.ts +108 -0
  132. package/src/pi-intercom/bridge-session.ts +2 -2
  133. package/src/pi-intercom/broker/broker.ts +27 -23
  134. package/src/pi-intercom/broker/client-last.ts +25 -12
  135. package/src/pi-intercom/broker/client.ts +55 -33
  136. package/src/pi-intercom/broker/framing.ts +11 -8
  137. package/src/pi-intercom/broker/guard.ts +33 -0
  138. package/src/pi-intercom/broker/last.ts +86 -33
  139. package/src/pi-intercom/broker/paths.ts +39 -12
  140. package/src/pi-intercom/broker/pid.ts +6 -1
  141. package/src/pi-intercom/broker/server-connection.ts +25 -0
  142. package/src/pi-intercom/connection.ts +21 -10
  143. package/src/pi-intercom/identity.ts +8 -5
  144. package/src/pi-intercom/inbound-delivery.ts +20 -66
  145. package/src/pi-intercom/inbound-journal.ts +103 -7
  146. package/src/pi-intercom/inbound-record.ts +21 -4
  147. package/src/pi-intercom/inbound.ts +18 -49
  148. package/src/pi-intercom/index.ts +3 -0
  149. package/src/pi-intercom/last-message.ts +10 -19
  150. package/src/pi-intercom/lifecycle.ts +117 -14
  151. package/src/pi-intercom/outbound.ts +7 -16
  152. package/src/pi-intercom/reply-tracker.ts +8 -6
  153. package/src/pi-intercom/reply-wait.ts +3 -2
  154. package/src/pi-intercom/runtime-types.ts +0 -1
  155. package/src/pi-intercom/tool-registration.ts +4 -1
  156. package/src/pi-intercom/tool-results.ts +10 -9
  157. package/src/pi-intercom/tool-schemas.ts +12 -44
  158. package/src/pi-intercom/types.ts +115 -4
  159. package/src/pi-intercom/validation.ts +4 -0
  160. package/src/shared/bounded-json.ts +162 -0
  161. package/src/shared/compact-cards.ts +118 -0
  162. package/src/shared/journal-frames.ts +3 -19
  163. package/src/shared/json-projection.ts +267 -147
  164. package/src/shared/native-journal.ts +157 -75
  165. package/src/shared/post-exit-stdio-guard.ts +165 -7
  166. package/src/shared/tui-action.ts +39 -0
  167. package/tsconfig.client.json +8 -0
  168. package/tsconfig.json +1 -0
  169. package/src/shared/stream-json.d.ts +0 -10
@@ -1,65 +1,116 @@
1
- import { jsonParser } from "stream-json/core/parser.js";
2
- import { none } from "stream-chain/defs.js";
3
1
  import { isRecord } from "./unknown.js";
2
+ // Grammar positions between tokens, then the two scalar states that can span chunks.
3
+ const VALUE = 0;
4
+ const FIRST_VALUE = 1;
5
+ const KEY = 2;
6
+ const FIRST_KEY = 3;
7
+ const COLON = 4;
8
+ const AFTER = 5;
9
+ const DONE = 6;
10
+ const STRING = 7;
11
+ const NUMBER = 8;
12
+ // Unescaped units exclude quote, backslash and C0 controls. Native regexp code skips a bounded
13
+ // run of them and valid escapes, so JavaScript handles only the unit that stopped it.
14
+ const BODY = /[ !#-[\]-\uffff]*(?:\\(?:["\\/bfnrt]|u[\da-fA-F]{4})[ !#-[\]-\uffff]*){0,1024}/y;
15
+ const LITERALS = new Map([
16
+ [102, ["false", false]],
17
+ [110, ["null", null]],
18
+ [116, ["true", true]],
19
+ ]);
20
+ // Number states: start, sign, zero, integer, point, fraction, e, exponent sign, exponent.
21
+ // Columns: "0", "1"-"9", ".", "e"/"E", "+"/"-". A missing transition ends the number;
22
+ // only "-" reaches the start state's sign column because values dispatch on their first unit.
23
+ const NUMBER_NEXT = [
24
+ [2, 3, -1, -1, 1],
25
+ [2, 3],
26
+ [-1, -1, 4, 6],
27
+ [3, 3, 4, 6],
28
+ [5, 5],
29
+ [5, 5, -1, 6],
30
+ [8, 8, -1, -1, 7],
31
+ [8, 8],
32
+ [8, 8],
33
+ ];
34
+ const NUMBER_END = new Set([2, 3, 5, 8]);
35
+ const NUMBER_COLUMN = new Int8Array(128).fill(-1);
36
+ for (const [column, units] of ["0", "123456789", ".", "eE", "+-"].entries()) {
37
+ for (const unit of units) {
38
+ NUMBER_COLUMN[unit.charCodeAt(0)] = column;
39
+ }
40
+ }
41
+ /**
42
+ * Classify where BODY stopped short of a closing quote. True means the chunk ends at or inside an
43
+ * escape, so the rest carries; false means a bounded run ended at a valid escape. Controls throw,
44
+ * and so does a complete escape BODY made no progress at, because BODY consumes any valid one.
45
+ */
46
+ function chunkEnds(text, end, progressed) {
47
+ if (end === text.length) {
48
+ return true;
49
+ }
50
+ if (text.charCodeAt(end) !== 92) {
51
+ throw new SyntaxError("Bad control character in JSON string");
52
+ }
53
+ if (end + (text.charCodeAt(end + 1) === 117 ? 6 : 2) > text.length) {
54
+ return true;
55
+ }
56
+ if (!progressed) {
57
+ throw new SyntaxError("Bad escaped character in JSON string");
58
+ }
59
+ return false;
60
+ }
61
+ function unexpected() {
62
+ return new SyntaxError("Unexpected character in JSON");
63
+ }
4
64
  function retained(keep) {
5
65
  return keep === true || (typeof keep === "number" && keep !== 0 && !Number.isNaN(keep));
6
66
  }
7
- /** Validate all tokens, assembling selected values only. Skipped strings remain unpacked. */
67
+ /** Validate all JSON text, assembling selected values only. Skipped strings are never decoded. */
8
68
  export class JsonProjection {
9
- parse = jsonParser({ packValues: false });
10
69
  stack = [];
11
- scalar;
70
+ state = VALUE;
71
+ carry = "";
72
+ path = [];
12
73
  key = false;
13
- keyLimit = 4096;
14
- nodes = 0;
74
+ textCap = 0;
75
+ text = "";
76
+ numberState = 0;
15
77
  select;
16
78
  stringChunk;
17
79
  keys;
18
- limits;
19
80
  value;
20
- constructor(select, stringChunk, keys = () => 4096, limits) {
81
+ constructor(select, stringChunk, keys = () => 4096) {
21
82
  this.select = select;
22
83
  this.stringChunk = stringChunk;
23
84
  this.keys = keys;
24
- this.limits = limits;
25
85
  }
26
86
  root() {
27
87
  return isRecord(this.value) ? this.value : undefined;
28
88
  }
29
- parent() {
30
- const parent = this.stack.at(-1);
31
- if (!parent) {
32
- throw new SyntaxError("JSON token requires a containing object");
33
- }
34
- return parent;
35
- }
36
- selected(path) {
37
- const parent = this.stack.at(-1);
38
- const keep = parent && parent.value === undefined ? false : this.select(path, this.root());
39
- if (retained(keep) && this.limits && ++this.nodes > this.limits.nodes) {
40
- throw new RangeError("JSON projection exceeds its assembled-node budget.");
41
- }
42
- return keep;
89
+ selected() {
90
+ return this.select(this.path, this.root());
43
91
  }
44
- nextPath() {
92
+ /** Paths inside skipped containers are only built for string observers, never selected. */
93
+ start() {
45
94
  const parent = this.stack.at(-1);
46
- if (this.limits &&
47
- (this.stack.length > this.limits.depth || (parent && parent.index >= this.limits.arrayLength))) {
48
- throw new RangeError("JSON projection exceeds its depth or array-position budget.");
49
- }
50
95
  if (!parent) {
51
- return [];
96
+ this.path = [];
97
+ return this.selected();
52
98
  }
53
- return [...parent.path, parent.index >= 0 ? parent.index++ : parent.key];
99
+ const step = parent.array ? parent.index++ : parent.key;
100
+ if (parent.value !== undefined) {
101
+ this.path = [...parent.path, step];
102
+ return this.selected();
103
+ }
104
+ this.path = this.stringChunk ? [...parent.path, step] : parent.path;
105
+ return false;
54
106
  }
55
- put(path, value) {
107
+ put(value) {
56
108
  const parent = this.stack.at(-1);
109
+ const key = this.path.at(-1);
57
110
  if (!parent) {
58
111
  this.value = value;
59
- return;
60
112
  }
61
- const key = path.at(-1);
62
- if (parent.value !== undefined && key !== undefined) {
113
+ else if (parent.value !== undefined && key !== undefined) {
63
114
  Object.defineProperty(parent.value, key, {
64
115
  value,
65
116
  enumerable: true,
@@ -68,124 +119,211 @@ export class JsonProjection {
68
119
  });
69
120
  }
70
121
  }
71
- startContainer(array) {
72
- const path = this.nextPath();
73
- const keep = this.selected(path);
122
+ ended() {
123
+ this.state = this.stack.length === 0 ? DONE : AFTER;
124
+ }
125
+ close(index) {
126
+ this.stack.pop();
127
+ this.ended();
128
+ return index + 1;
129
+ }
130
+ scalar(state, key, keep) {
131
+ const cap = keep === true ? Infinity : Number(keep);
132
+ this.state = state;
133
+ this.key = key;
134
+ this.textCap = Number.isNaN(cap) ? 0 : cap;
135
+ this.text = "";
136
+ this.numberState = 0;
137
+ }
138
+ append(text) {
139
+ if (this.text.length < this.textCap) {
140
+ this.text += text.slice(0, this.textCap - this.text.length);
141
+ }
142
+ }
143
+ container(array) {
74
144
  let value;
75
- if (retained(keep)) {
145
+ if (retained(this.start())) {
76
146
  value = array ? [] : {};
77
- this.put(path, value);
147
+ this.put(value);
78
148
  }
79
- this.stack.push({ path, value, key: "", index: array ? 0 : -1 });
149
+ this.stack.push({ path: this.path, value, array, key: "", index: 0 });
150
+ this.state = array ? FIRST_VALUE : FIRST_KEY;
80
151
  }
81
- startScalar(number) {
82
- const path = this.nextPath();
83
- const keep = this.selected(path);
84
- const selectedLimit = keep === true ? Infinity : Number(keep);
85
- const limit = Number.isNaN(selectedLimit) ? 0 : selectedLimit;
86
- this.scalar = { path, text: "", limit, number };
152
+ literal(text, index, code) {
153
+ const literal = LITERALS.get(code);
154
+ if (!literal) {
155
+ throw unexpected();
156
+ }
157
+ const [word, value] = literal;
158
+ if (text.startsWith(word, index)) {
159
+ if (retained(this.start())) {
160
+ this.put(value);
161
+ }
162
+ this.ended();
163
+ return index + word.length;
164
+ }
165
+ if (text.length - index < word.length && word.startsWith(text.slice(index))) {
166
+ this.carry = text.slice(index);
167
+ return text.length;
168
+ }
169
+ throw unexpected();
87
170
  }
88
- appendKey(text) {
89
- const parent = this.parent();
90
- if (parent.value !== undefined &&
91
- this.limits &&
92
- parent.key.length + text.length > this.limits.keyLength) {
93
- throw new RangeError("JSON projection exceeds its property-key budget.");
171
+ startValue(text, index, code) {
172
+ if (code === 34) {
173
+ this.scalar(STRING, false, this.start());
174
+ return index + 1;
94
175
  }
95
- if (parent.key.length < this.keyLimit) {
96
- parent.key += text.slice(0, this.keyLimit - parent.key.length);
176
+ if (code === 45 || (code >= 48 && code <= 57)) {
177
+ this.scalar(NUMBER, false, this.start());
178
+ return index;
97
179
  }
180
+ if (code === 123 || code === 91) {
181
+ this.container(code === 91);
182
+ return index + 1;
183
+ }
184
+ return this.literal(text, index, code);
98
185
  }
99
- appendScalar(text, string) {
100
- const scalar = this.scalar;
101
- if (!scalar) {
102
- return;
186
+ member(index, code) {
187
+ const parent = this.stack.at(-1);
188
+ if (code === 125 && this.state === FIRST_KEY) {
189
+ return this.close(index);
190
+ }
191
+ if (code !== 34 || !parent) {
192
+ throw unexpected();
103
193
  }
104
- if (string) {
105
- this.stringChunk?.(scalar.path, text, this.root());
194
+ this.scalar(STRING, true, parent.value === undefined ? 0 : this.keys(parent.path, this.root()));
195
+ return index + 1;
196
+ }
197
+ after(index, code) {
198
+ const parent = this.stack.at(-1);
199
+ if (parent && code === 44) {
200
+ this.state = parent.array ? VALUE : KEY;
201
+ return index + 1;
106
202
  }
107
- if (scalar.text.length < scalar.limit) {
108
- scalar.text += text.slice(0, scalar.limit - scalar.text.length);
203
+ if (parent && code === (parent.array ? 93 : 125)) {
204
+ return this.close(index);
109
205
  }
206
+ throw unexpected();
110
207
  }
111
- finishScalar() {
112
- const scalar = this.scalar;
113
- if (scalar && scalar.limit !== 0) {
114
- this.put(scalar.path, scalar.number ? Number(scalar.text) : scalar.text);
208
+ structure(text, index, code) {
209
+ switch (this.state) {
210
+ case VALUE:
211
+ return this.startValue(text, index, code);
212
+ case FIRST_VALUE:
213
+ return code === 93 ? this.close(index) : this.startValue(text, index, code);
214
+ case KEY:
215
+ case FIRST_KEY:
216
+ return this.member(index, code);
217
+ case COLON:
218
+ if (code !== 58) {
219
+ throw unexpected();
220
+ }
221
+ this.state = VALUE;
222
+ return index + 1;
223
+ case AFTER:
224
+ return this.after(index, code);
225
+ default:
226
+ throw unexpected();
115
227
  }
116
- this.scalar = undefined;
117
228
  }
118
- token(token) {
119
- switch (token.name) {
120
- case "startKey": {
121
- const parent = this.parent();
122
- this.key = true;
123
- parent.key = "";
124
- this.keyLimit = parent.value === undefined ? 0 : this.keys(parent.path, this.root());
125
- break;
229
+ observed(decoded) {
230
+ if (decoded === "") {
231
+ return;
232
+ }
233
+ if (this.stringChunk && !this.key) {
234
+ this.stringChunk(this.path, decoded, this.root());
235
+ }
236
+ this.append(decoded);
237
+ }
238
+ closeString() {
239
+ const parent = this.stack.at(-1);
240
+ if (this.key && parent) {
241
+ parent.key = this.text;
242
+ this.state = COLON;
243
+ }
244
+ else {
245
+ if (this.textCap !== 0) {
246
+ this.put(this.text);
126
247
  }
127
- case "endKey":
128
- this.key = false;
129
- break;
130
- case "startObject":
131
- this.startContainer(false);
132
- break;
133
- case "startArray":
134
- this.startContainer(true);
135
- break;
136
- case "endObject":
137
- case "endArray":
138
- this.stack.pop();
139
- break;
140
- case "startString":
141
- this.startScalar(false);
142
- break;
143
- case "startNumber":
144
- this.startScalar(true);
145
- break;
146
- case "stringChunk":
147
- case "numberChunk":
148
- if (this.key) {
149
- this.appendKey(token.value);
150
- }
151
- else {
152
- this.appendScalar(token.value, token.name === "stringChunk");
153
- }
154
- break;
155
- case "endString":
156
- case "endNumber":
157
- this.finishScalar();
158
- break;
159
- case "trueValue":
160
- case "falseValue":
161
- case "nullValue": {
162
- const path = this.nextPath();
163
- const keep = this.selected(path);
164
- if (retained(keep)) {
165
- this.put(path, token.value);
166
- }
248
+ this.ended();
249
+ }
250
+ }
251
+ /** Observed values and unfinished selections need text; others are only validated. */
252
+ decoding() {
253
+ return (this.stringChunk !== undefined && !this.key) || this.text.length < this.textCap;
254
+ }
255
+ string(text, start) {
256
+ for (let index = start;;) {
257
+ BODY.lastIndex = index;
258
+ BODY.test(text);
259
+ const end = BODY.lastIndex;
260
+ if (end > index && this.decoding()) {
261
+ const raw = text.slice(index, end);
262
+ // A matched run is a complete valid string body no longer than this chunk.
263
+ this.observed(raw.includes("\\") ? String(JSON.parse(`"${raw}"`)) : raw);
264
+ }
265
+ if (text.charCodeAt(end) === 34) {
266
+ this.closeString();
267
+ return end + 1;
268
+ }
269
+ if (chunkEnds(text, end, end > index)) {
270
+ this.carry = text.slice(end);
271
+ return text.length;
272
+ }
273
+ index = end;
274
+ }
275
+ }
276
+ number(text, start, final) {
277
+ let state = this.numberState;
278
+ let index = start;
279
+ for (; index < text.length; index++) {
280
+ const next = NUMBER_NEXT[state]?.[NUMBER_COLUMN[text.charCodeAt(index)] ?? -1] ?? -1;
281
+ if (next < 0) {
167
282
  break;
168
283
  }
169
- case "keyValue":
170
- case "stringValue":
171
- case "numberValue":
172
- case "whitespace":
173
- break; // Packing is disabled; whitespace carries no projected value.
284
+ state = next;
285
+ }
286
+ this.numberState = state;
287
+ this.append(text.slice(start, index));
288
+ if (index === text.length && !final) {
289
+ return index;
174
290
  }
291
+ if (!NUMBER_END.has(state)) {
292
+ throw new SyntaxError("Incomplete number in JSON");
293
+ }
294
+ if (this.textCap !== 0) {
295
+ this.put(Number(this.text));
296
+ }
297
+ this.ended();
298
+ return index;
175
299
  }
176
300
  write(chunk) {
177
- if (typeof chunk === "symbol" && chunk !== none) {
178
- throw new TypeError("Unknown JSON tokenizer sentinel");
179
- }
180
- const output = this.parse(typeof chunk === "symbol" ? none : chunk);
181
- if (typeof output !== "symbol") {
182
- for (const token of output.values) {
183
- this.token(token);
301
+ const text = this.carry + chunk;
302
+ this.carry = "";
303
+ let index = 0;
304
+ while (index < text.length) {
305
+ const code = text.charCodeAt(index);
306
+ if (this.state === STRING) {
307
+ index = this.string(text, index);
308
+ }
309
+ else if (this.state === NUMBER) {
310
+ index = this.number(text, index, false);
311
+ }
312
+ else if (code === 32 || code === 10 || code === 13 || code === 9) {
313
+ index++;
314
+ }
315
+ else {
316
+ index = this.structure(text, index, code);
184
317
  }
185
318
  }
186
319
  }
187
320
  finish() {
188
- this.write(none);
321
+ if (this.state === NUMBER) {
322
+ this.number("", 0, true);
323
+ }
324
+ if (this.state !== DONE || this.carry !== "") {
325
+ throw new SyntaxError("Unexpected end of JSON input");
326
+ }
189
327
  return this.value;
190
328
  }
191
329
  }