@forgeax/engine-intelligence 0.1.4 → 0.1.7

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.
package/src/errors.ts CHANGED
@@ -1,5 +1,14 @@
1
1
  import type { ActivityId } from './types';
2
2
 
3
+ export type IntelligenceErrorCode =
4
+ | 'intelligence-invalid-request'
5
+ | 'intelligence-session-provider-mismatch'
6
+ | 'intelligence-capacity-exceeded'
7
+ | 'intelligence-activity-not-found'
8
+ | 'intelligence-provider-failed'
9
+ | 'intelligence-output-overflow'
10
+ | 'intelligence-closed';
11
+
3
12
  export interface IntelligenceErrorDetailMap {
4
13
  readonly 'intelligence-invalid-request': { readonly field: 'input'; readonly reason: string };
5
14
  readonly 'intelligence-session-provider-mismatch': {
@@ -17,8 +26,6 @@ export interface IntelligenceErrorDetailMap {
17
26
  readonly 'intelligence-closed': Readonly<Record<string, never>>;
18
27
  }
19
28
 
20
- export type IntelligenceErrorCode = keyof IntelligenceErrorDetailMap;
21
-
22
29
  export type IntelligenceErrorDetailFor<C extends IntelligenceErrorCode> =
23
30
  IntelligenceErrorDetailMap[C];
24
31
 
package/src/runtime.ts CHANGED
@@ -162,7 +162,11 @@ export class IntelligenceRuntime implements IntelligenceService {
162
162
 
163
163
  private async performClose(): Promise<void> {
164
164
  this.closed = true;
165
- await this.provider.close();
165
+ try {
166
+ await this.provider.close();
167
+ } catch {
168
+ // Disposal is terminal; provider failure must not strand the realm transport.
169
+ }
166
170
  this.records.clear();
167
171
  }
168
172
 
@@ -205,7 +209,7 @@ export class IntelligenceRuntime implements IntelligenceService {
205
209
  private createSink(record: ActivityRecord): ActivitySink {
206
210
  return {
207
211
  text: (text) => {
208
- if (record.terminal || text.length === 0) return;
212
+ if (this.closed || record.terminal || text.length === 0) return;
209
213
  if (record.outputChars + text.length > this.limits.maxOutputChars) {
210
214
  this.overflow(record, 'output-chars', this.limits.maxOutputChars);
211
215
  return;
@@ -218,7 +222,7 @@ export class IntelligenceRuntime implements IntelligenceService {
218
222
  this.push(record, { type: 'text-delta', text });
219
223
  },
220
224
  complete: (output) => {
221
- if (record.terminal) return;
225
+ if (this.closed || record.terminal) return;
222
226
  if (output.length > this.limits.maxOutputChars) {
223
227
  this.overflow(record, 'output-chars', this.limits.maxOutputChars);
224
228
  return;
@@ -227,13 +231,13 @@ export class IntelligenceRuntime implements IntelligenceService {
227
231
  this.push(record, { type: 'completed', session: record.ref.session, output });
228
232
  },
229
233
  fail: (cause) => {
230
- if (record.terminal) return;
234
+ if (this.closed || record.terminal) return;
231
235
  record.terminal = true;
232
236
  const error = providerError(this.providerId, cause);
233
237
  this.push(record, { type: 'failed', error: intelligenceFailure(error) });
234
238
  },
235
239
  cancelled: () => {
236
- if (record.terminal) return;
240
+ if (this.closed || record.terminal) return;
237
241
  record.terminal = true;
238
242
  this.push(record, { type: 'cancelled' });
239
243
  },
package/src/transport.ts CHANGED
@@ -52,9 +52,11 @@ export function bindIntelligencePort(
52
52
  runtime: IntelligenceRuntime,
53
53
  ): IntelligencePortBinding {
54
54
  let closed = false;
55
+ let closeTask: Promise<void> | undefined;
55
56
  const listener = (
56
57
  event: MessageEvent<IntelligenceHostCommand | IntelligenceRealmMessage>,
57
58
  ): void => {
59
+ if (closed) return;
58
60
  const message = event.data;
59
61
  if (message.kind === 'intelligence-submit') {
60
62
  const accepted = runtime.accept(message.submission);
@@ -76,19 +78,18 @@ export function bindIntelligencePort(
76
78
  runtime.cancel(message.activityId);
77
79
  return;
78
80
  }
79
- if (message.kind === 'intelligence-close') void close(true);
81
+ if (message.kind === 'intelligence-close') void close();
80
82
  };
81
- const close = async (notifyRealm = false): Promise<void> => {
82
- if (closed) return;
83
+ const close = (): Promise<void> => {
84
+ if (closeTask !== undefined) return closeTask;
83
85
  closed = true;
84
86
  port.removeEventListener('message', listener);
85
- await runtime.close();
86
- if (notifyRealm) {
87
+ closeTask = (async () => {
88
+ await runtime.close();
87
89
  port.postMessage({ kind: 'intelligence-closed' });
88
90
  setTimeout(() => port.close(), 0);
89
- } else {
90
- port.close();
91
- }
91
+ })();
92
+ return closeTask;
92
93
  };
93
94
  port.addEventListener('message', listener);
94
95
  port.start?.();
@@ -157,6 +158,7 @@ export class IntelligencePortClient implements IntelligenceService {
157
158
  this.createSessionId = options.createSessionId ?? (() => nextPortIdentity('session'));
158
159
  this.listener = (event) => {
159
160
  const message = event.data;
161
+ if (this.closed && message.kind !== 'intelligence-closed') return;
160
162
  if (message.kind === 'intelligence-events') {
161
163
  this.pollPending = false;
162
164
  for (const item of message.events) {
@@ -167,19 +169,26 @@ export class IntelligencePortClient implements IntelligenceService {
167
169
  this.active.delete(message.activityId);
168
170
  this.rejected.set(message.activityId, message);
169
171
  } else if (message.kind === 'intelligence-closed') {
172
+ this.markClosed();
170
173
  this.port.removeEventListener('message', this.listener);
171
- this.received.length = 0;
172
- this.active.clear();
173
- this.rejected.clear();
174
174
  this.port.close();
175
175
  this.closeResolve?.();
176
176
  this.closeResolve = undefined;
177
+ this.closeTask ??= Promise.resolve();
177
178
  }
178
179
  };
179
180
  port.addEventListener('message', this.listener);
180
181
  port.start?.();
181
182
  }
182
183
 
184
+ private markClosed(): void {
185
+ this.closed = true;
186
+ this.received.length = 0;
187
+ this.active.clear();
188
+ this.rejected.clear();
189
+ this.pollPending = false;
190
+ }
191
+
183
192
  submit(request: ActivityRequest): Result<ActivityRef, IntelligenceError> {
184
193
  if (this.closed) return err(new IntelligenceError({ code: 'intelligence-closed', detail: {} }));
185
194
  if (request.input.length === 0 || request.input.length > this.limits.maxInputChars) {
@@ -264,7 +273,11 @@ export class IntelligencePortClient implements IntelligenceService {
264
273
 
265
274
  close(): Promise<void> {
266
275
  if (this.closeTask !== undefined) return this.closeTask;
267
- this.closed = true;
276
+ if (this.closed) {
277
+ this.closeTask = Promise.resolve();
278
+ return this.closeTask;
279
+ }
280
+ this.markClosed();
268
281
  this.closeTask = new Promise((resolve) => {
269
282
  this.closeResolve = resolve;
270
283
  this.port.postMessage({ kind: 'intelligence-close' });
@@ -1,33 +0,0 @@
1
- import type { IntelligenceErrorCode, IntelligenceErrorDetailMap } from '../errors';
2
- import type { INTELLIGENCE_ERROR_HINTS, INTELLIGENCE_EXPECTED, IntelligenceErrorDetailFor, IntelligenceFailure, IntelligenceError as PublicIntelligenceError, IntelligenceErrorCode as PublicIntelligenceErrorCode } from '../index';
3
- type Equal<Left, Right> = (<Type>() => Type extends Left ? 1 : 2) extends <Type>() => Type extends Right ? 1 : 2 ? true : false;
4
- type Assert<Value extends true> = Value;
5
- type ExpectedCodes = 'intelligence-invalid-request' | 'intelligence-session-provider-mismatch' | 'intelligence-capacity-exceeded' | 'intelligence-activity-not-found' | 'intelligence-provider-failed' | 'intelligence-output-overflow' | 'intelligence-closed';
6
- type _CodeIsDetailMapKeys = Assert<Equal<IntelligenceErrorCode, keyof IntelligenceErrorDetailMap>>;
7
- type _CodeHasExactMembership = Assert<Equal<IntelligenceErrorCode, ExpectedCodes>>;
8
- type _PublicCodeIsOwnerCode = Assert<Equal<PublicIntelligenceErrorCode, IntelligenceErrorCode>>;
9
- type _ExpectedKeysAreCodes = Assert<Equal<keyof typeof INTELLIGENCE_EXPECTED, IntelligenceErrorCode>>;
10
- type _HintKeysAreCodes = Assert<Equal<keyof typeof INTELLIGENCE_ERROR_HINTS, IntelligenceErrorCode>>;
11
- type _DetailProjectionUsesTheMap = Assert<Equal<IntelligenceErrorDetailFor<'intelligence-output-overflow'>, IntelligenceErrorDetailMap['intelligence-output-overflow']>>;
12
- declare function readErrorDetail(error: PublicIntelligenceError): string;
13
- declare function readFailure(error: IntelligenceFailure): string;
14
- export type _IntelligenceErrorCodeOwnerChecks = {
15
- /** @internal */
16
- _mapKeys: _CodeIsDetailMapKeys;
17
- /** @internal */
18
- _membership: _CodeHasExactMembership;
19
- /** @internal */
20
- _publicCode: _PublicCodeIsOwnerCode;
21
- /** @internal */
22
- _expectedKeys: _ExpectedKeysAreCodes;
23
- /** @internal */
24
- _hintKeys: _HintKeysAreCodes;
25
- /** @internal */
26
- _detailProjection: _DetailProjectionUsesTheMap;
27
- /** @internal */
28
- _readErrorDetail: typeof readErrorDetail;
29
- /** @internal */
30
- _readFailure: typeof readFailure;
31
- };
32
- export {};
33
- //# sourceMappingURL=intelligence-error-code-owner.test-d.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"intelligence-error-code-owner.test-d.d.ts","sourceRoot":"","sources":["../../src/__tests__/intelligence-error-code-owner.test-d.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,KAAK,EACV,wBAAwB,EACxB,qBAAqB,EACrB,0BAA0B,EAC1B,mBAAmB,EACnB,iBAAiB,IAAI,uBAAuB,EAC5C,qBAAqB,IAAI,2BAA2B,EACrD,MAAM,UAAU,CAAC;AAGlB,KAAK,KAAK,CAAC,IAAI,EAAE,KAAK,IACpB,CAAC,CAAC,IAAI,OAAO,IAAI,SAAS,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,IAAI,OAAO,IAAI,SAAS,KAAK,GAAG,CAAC,GAAG,CAAC,GAClF,IAAI,GACJ,KAAK,CAAC;AAEZ,KAAK,MAAM,CAAC,KAAK,SAAS,IAAI,IAAI,KAAK,CAAC;AAExC,KAAK,aAAa,GACd,8BAA8B,GAC9B,wCAAwC,GACxC,gCAAgC,GAChC,iCAAiC,GACjC,8BAA8B,GAC9B,8BAA8B,GAC9B,qBAAqB,CAAC;AAE1B,KAAK,oBAAoB,GAAG,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,MAAM,0BAA0B,CAAC,CAAC,CAAC;AACnG,KAAK,uBAAuB,GAAG,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,aAAa,CAAC,CAAC,CAAC;AACnF,KAAK,sBAAsB,GAAG,MAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE,qBAAqB,CAAC,CAAC,CAAC;AAChG,KAAK,qBAAqB,GAAG,MAAM,CACjC,KAAK,CAAC,MAAM,OAAO,qBAAqB,EAAE,qBAAqB,CAAC,CACjE,CAAC;AACF,KAAK,iBAAiB,GAAG,MAAM,CAC7B,KAAK,CAAC,MAAM,OAAO,wBAAwB,EAAE,qBAAqB,CAAC,CACpE,CAAC;AACF,KAAK,2BAA2B,GAAG,MAAM,CACvC,KAAK,CACH,0BAA0B,CAAC,8BAA8B,CAAC,EAC1D,0BAA0B,CAAC,8BAA8B,CAAC,CAC3D,CACF,CAAC;AAgCF,iBAAS,eAAe,CAAC,KAAK,EAAE,uBAAuB,GAAG,MAAM,CAqB/D;AAED,iBAAS,WAAW,CAAC,KAAK,EAAE,mBAAmB,GAAG,MAAM,CAmBvD;AAUD,MAAM,MAAM,iCAAiC,GAAG;IAC9C,gBAAgB;IAChB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,gBAAgB;IAChB,WAAW,EAAE,uBAAuB,CAAC;IACrC,gBAAgB;IAChB,WAAW,EAAE,sBAAsB,CAAC;IACpC,gBAAgB;IAChB,aAAa,EAAE,qBAAqB,CAAC;IACrC,gBAAgB;IAChB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,gBAAgB;IAChB,iBAAiB,EAAE,2BAA2B,CAAC;IAC/C,gBAAgB;IAChB,gBAAgB,EAAE,OAAO,eAAe,CAAC;IACzC,gBAAgB;IAChB,YAAY,EAAE,OAAO,WAAW,CAAC;CAClC,CAAC"}
@@ -1,143 +0,0 @@
1
- import type { IntelligenceErrorCode, IntelligenceErrorDetailMap } from '../errors';
2
- import type {
3
- INTELLIGENCE_ERROR_HINTS,
4
- INTELLIGENCE_EXPECTED,
5
- IntelligenceErrorDetailFor,
6
- IntelligenceFailure,
7
- IntelligenceError as PublicIntelligenceError,
8
- IntelligenceErrorCode as PublicIntelligenceErrorCode,
9
- } from '../index';
10
- import { activityId, IntelligenceError, intelligenceFailure, providerError } from '../index';
11
-
12
- type Equal<Left, Right> =
13
- (<Type>() => Type extends Left ? 1 : 2) extends <Type>() => Type extends Right ? 1 : 2
14
- ? true
15
- : false;
16
-
17
- type Assert<Value extends true> = Value;
18
-
19
- type ExpectedCodes =
20
- | 'intelligence-invalid-request'
21
- | 'intelligence-session-provider-mismatch'
22
- | 'intelligence-capacity-exceeded'
23
- | 'intelligence-activity-not-found'
24
- | 'intelligence-provider-failed'
25
- | 'intelligence-output-overflow'
26
- | 'intelligence-closed';
27
-
28
- type _CodeIsDetailMapKeys = Assert<Equal<IntelligenceErrorCode, keyof IntelligenceErrorDetailMap>>;
29
- type _CodeHasExactMembership = Assert<Equal<IntelligenceErrorCode, ExpectedCodes>>;
30
- type _PublicCodeIsOwnerCode = Assert<Equal<PublicIntelligenceErrorCode, IntelligenceErrorCode>>;
31
- type _ExpectedKeysAreCodes = Assert<
32
- Equal<keyof typeof INTELLIGENCE_EXPECTED, IntelligenceErrorCode>
33
- >;
34
- type _HintKeysAreCodes = Assert<
35
- Equal<keyof typeof INTELLIGENCE_ERROR_HINTS, IntelligenceErrorCode>
36
- >;
37
- type _DetailProjectionUsesTheMap = Assert<
38
- Equal<
39
- IntelligenceErrorDetailFor<'intelligence-output-overflow'>,
40
- IntelligenceErrorDetailMap['intelligence-output-overflow']
41
- >
42
- >;
43
-
44
- const acceptsCode = (code: PublicIntelligenceErrorCode): PublicIntelligenceErrorCode => code;
45
- acceptsCode('intelligence-closed');
46
- // @ts-expect-error unknown literals remain outside the closed public code union.
47
- acceptsCode('intelligence-not-real');
48
-
49
- const overflow = new IntelligenceError({
50
- code: 'intelligence-output-overflow',
51
- detail: {
52
- activityId: activityId('overflow'),
53
- bound: 'output-chars',
54
- limit: 256,
55
- },
56
- });
57
- type OverflowError = Extract<
58
- PublicIntelligenceError,
59
- { readonly code: 'intelligence-output-overflow' }
60
- >;
61
- const typedOverflow: OverflowError = overflow;
62
- const expected: string = overflow.expected;
63
- const hint: string = overflow.hint;
64
- void typedOverflow;
65
- void expected;
66
- void hint;
67
-
68
- new IntelligenceError({
69
- code: 'intelligence-capacity-exceeded',
70
- // @ts-expect-error the detail must match the selected code.
71
- detail: { activityId: activityId('wrong-detail') },
72
- });
73
-
74
- function readErrorDetail(error: PublicIntelligenceError): string {
75
- switch (error.code) {
76
- case 'intelligence-invalid-request':
77
- // @ts-expect-error code-driven narrowing excludes unrelated detail fields.
78
- void error.detail.limit;
79
- return `${error.detail.field}:${error.detail.reason}`;
80
- case 'intelligence-session-provider-mismatch':
81
- return `${error.detail.expectedProviderId}:${error.detail.receivedProviderId}`;
82
- case 'intelligence-capacity-exceeded':
83
- return String(error.detail.limit);
84
- case 'intelligence-activity-not-found':
85
- return error.detail.activityId;
86
- case 'intelligence-provider-failed':
87
- return `${error.detail.providerId}:${String(error.detail.cause)}`;
88
- case 'intelligence-output-overflow':
89
- return `${error.detail.activityId}:${error.detail.bound}:${error.detail.limit}`;
90
- case 'intelligence-closed':
91
- return Object.keys(error.detail).join(',');
92
- }
93
- const exhaustive: never = error;
94
- return exhaustive;
95
- }
96
-
97
- function readFailure(error: IntelligenceFailure): string {
98
- switch (error.code) {
99
- case 'intelligence-invalid-request':
100
- return `${error.detail.field}:${error.detail.reason}`;
101
- case 'intelligence-session-provider-mismatch':
102
- return `${error.detail.expectedProviderId}:${error.detail.receivedProviderId}`;
103
- case 'intelligence-capacity-exceeded':
104
- return String(error.detail.limit);
105
- case 'intelligence-activity-not-found':
106
- return error.detail.activityId;
107
- case 'intelligence-provider-failed':
108
- return `${error.detail.providerId}:${error.detail.cause}`;
109
- case 'intelligence-output-overflow':
110
- return `${error.detail.activityId}:${error.detail.bound}:${error.detail.limit}`;
111
- case 'intelligence-closed':
112
- return Object.keys(error.detail).join(',');
113
- }
114
- const exhaustive: never = error;
115
- return exhaustive;
116
- }
117
-
118
- const projectedFailure: IntelligenceFailure = intelligenceFailure(
119
- providerError('test.provider', new Error('failed')),
120
- );
121
- void acceptsCode;
122
- void readErrorDetail;
123
- void readFailure;
124
- void projectedFailure;
125
-
126
- export type _IntelligenceErrorCodeOwnerChecks = {
127
- /** @internal */
128
- _mapKeys: _CodeIsDetailMapKeys;
129
- /** @internal */
130
- _membership: _CodeHasExactMembership;
131
- /** @internal */
132
- _publicCode: _PublicCodeIsOwnerCode;
133
- /** @internal */
134
- _expectedKeys: _ExpectedKeysAreCodes;
135
- /** @internal */
136
- _hintKeys: _HintKeysAreCodes;
137
- /** @internal */
138
- _detailProjection: _DetailProjectionUsesTheMap;
139
- /** @internal */
140
- _readErrorDetail: typeof readErrorDetail;
141
- /** @internal */
142
- _readFailure: typeof readFailure;
143
- };