aki-pro-max 2.3.3 → 2.3.4

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 (61) hide show
  1. package/README.md +1 -1
  2. package/bin/aki-pro-max.js +1 -1
  3. package/package.json +1 -1
  4. package/src/admin.mjs +1 -1
  5. package/version.json +2 -2
  6. package/.env.example +0 -34
  7. package/AICOWORKER-NATIVE-TOOLS.md +0 -60
  8. package/CLAUDE-LIVE-TOOLS-EVIDENCE.json +0 -94
  9. package/FULL-TRACE-EVIDENCE.json +0 -109
  10. package/ISSUE-1-REMOTE.json +0 -1
  11. package/ISSUE-2-POSTREVIEW.json +0 -1
  12. package/ISSUE-2-REMOTE.json +0 -1
  13. package/KEY-ROTATION-EVIDENCE.json +0 -9
  14. package/PMN-9ROUTER-FINAL.md +0 -13
  15. package/RAPID-BASIL-RECONCILIATION.json +0 -10
  16. package/RELEASE-ARCHIVE.json +0 -15
  17. package/SECURITY-RECONCILIATION.json +0 -30
  18. package/TEST-ISSUES12-FINAL.txt +0 -0
  19. package/TEST-ISSUES12-HARNESS.txt +0 -0
  20. package/VERIFIER-ISSUES12-FINAL.txt +0 -0
  21. package/VERIFY-RELEASE-ISSUES12-FINAL.txt +0 -0
  22. package/VERIFY-RELEASE-ISSUES12-HARNESS.txt +0 -0
  23. package/docs/ADMIN-GUI-CONTRACT.md +0 -29
  24. package/docs/CAPABILITY-MATRIX.md +0 -44
  25. package/docs/CORRELATION-DESIGN.md +0 -226
  26. package/docs/FAIL-CLOSED-ISSUE-HARNESS.md +0 -21
  27. package/docs/WEB-SESSION-TRANSPORT-DESIGN.md +0 -423
  28. package/docs/assets/control-plane.jpg +0 -0
  29. package/gitleaks-report-all.json +0 -1
  30. package/gitleaks-report-latest.json +0 -1
  31. package/gitleaks-report.json +0 -1
  32. package/scripts/eventual-tool-loop.mjs +0 -55
  33. package/scripts/live-eventual-multitool.mjs +0 -18
  34. package/scripts/upgrade-admin-v232.mjs +0 -33
  35. package/scripts/verify-issue-closure.mjs +0 -81
  36. package/scripts/verify-release.mjs +0 -31
  37. package/test/9router-executor.integration.test.mjs +0 -207
  38. package/test/admin-auth.test.mjs +0 -47
  39. package/test/admin.test.mjs +0 -68
  40. package/test/config.test.mjs +0 -14
  41. package/test/contract.test.mjs +0 -14
  42. package/test/correlation-store.test.mjs +0 -19
  43. package/test/correlation.integration.test.mjs +0 -48
  44. package/test/eventual-tool-loop.test.mjs +0 -42
  45. package/test/fixtures/text.json +0 -8
  46. package/test/fixtures/tool.json +0 -7
  47. package/test/fixtures/web-session-observed-done.json +0 -12
  48. package/test/fixtures/web-session-tool-fragments.json +0 -14
  49. package/test/full-ingress/alias-loader.mjs +0 -22
  50. package/test/full-ingress/run-full-ingress.mjs +0 -207
  51. package/test/full-ingress/seed-9router.mjs +0 -36
  52. package/test/helpers.mjs +0 -9
  53. package/test/issue-closure-harness.test.mjs +0 -49
  54. package/test/model-thinking.test.mjs +0 -20
  55. package/test/protocol.test.mjs +0 -16
  56. package/test/request.test.mjs +0 -10
  57. package/test/session-store.test.mjs +0 -20
  58. package/test/web-session-builder.test.mjs +0 -124
  59. package/test/web-session-events.test.mjs +0 -241
  60. package/test/web-session-integration.test.mjs +0 -103
  61. package/test/web-session-tools.test.mjs +0 -52
@@ -1,124 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { loadConfig, webSessionUrl } from '../src/config.mjs';
4
- import { normalizeOpenAIRequest } from '../src/openai.mjs';
5
- import { buildPostmanRequest } from '../src/postman-request.mjs';
6
- import { buildWebSessionTextRequest } from '../src/web-session-request.mjs';
7
- import { createProviderServer } from '../src/server.mjs';
8
- import { listen, request } from './helpers.mjs';
9
-
10
- const base = {
11
- PROVIDER_API_KEY: 'facade-test-dummy',
12
- PROVIDER_MODELS: 'public-model',
13
- POSTMAN_TRANSPORT_STRATEGY: 'web_session',
14
- POSTMAN_SESSION_COOKIE: 'session-test-dummy',
15
- POSTMAN_WORKSPACE_SUBDOMAIN: 'team-7',
16
- POSTMAN_WORKSPACE_ID: 'workspace-test-dummy',
17
- POSTMAN_WEB_SESSION_SELECTED_MODEL: 'UPSTREAM_PIN_TEST',
18
- MAX_BODY_BYTES: '4096',
19
- };
20
- const config = () => loadConfig(base);
21
- const normalized = (body = {}) => normalizeOpenAIRequest({ model: 'public-model', messages: [{ role: 'system', content: 'be exact' }, { role: 'user', content: 'hello' }], ...body }, config());
22
-
23
- test('web_session config derives one exact HTTPS Postman chat URL', () => {
24
- const c = config();
25
- const url = webSessionUrl(c);
26
- assert.equal(url.href, 'https://team-7.postman.co/_gw/chat');
27
- assert.equal(url.origin, 'https://team-7.postman.co');
28
- assert.equal(url.hostname, 'team-7.postman.co');
29
- });
30
-
31
- test('strategy is closed, web-session fields require explicit strategy, and credentials are exclusive', () => {
32
- assert.throws(() => loadConfig({ ...base, POSTMAN_TRANSPORT_STRATEGY: 'other' }), /must be access_token or web_session/);
33
- const { POSTMAN_TRANSPORT_STRATEGY, ...implicit } = base;
34
- assert.throws(() => loadConfig(implicit), /STRATEGY is required/);
35
- assert.throws(() => loadConfig({ ...base, POSTMAN_ACCESS_TOKEN: 'token-test-dummy' }), /web_session forbids/);
36
- assert.throws(() => loadConfig({ ...base, POSTMAN_ORIGIN: 'https:\/\/gateway.example.test' }), /web_session forbids/);
37
- assert.throws(() => loadConfig({ ...base, POSTMAN_CHAT_PATH: '/chat' }), /web_session forbids/);
38
- const legacy = loadConfig({ PROVIDER_API_KEY: 'a', PROVIDER_MODELS: 'm', POSTMAN_ACCESS_TOKEN: 'b', POSTMAN_WORKSPACE_ID: 'w', POSTMAN_ORIGIN: 'https://gateway.example.test' });
39
- assert.equal(legacy.transportStrategy, 'access_token');
40
- assert.equal(legacy.transportStrategyExplicit, false);
41
- });
42
-
43
- test('cookie and subdomain reject injection, whole-cookie syntax, lookalikes, and bounds', () => {
44
- for (const value of ['x\rY: z', 'x\ny', 'x y', 'x;y', 'x,y', 'x\\y', '\0x', 'é', 'x'.repeat(4097)]) assert.throws(() => loadConfig({ ...base, POSTMAN_SESSION_COOKIE: value }), /POSTMAN_SESSION_COOKIE/);
45
- assert.equal(loadConfig({ ...base, POSTMAN_SESSION_COOKIE: 'x'.repeat(4096) }).webSessionCookie.length, 4096);
46
- for (const value of ['foo.bar', 'foo.postman.co', 'postman.co', 'foo%2ebar', 'Foo', 'foo_bar', '-foo', 'foo-', 'foo/', 'foo/bar', 'foo:443', 'foo:80', 'foo@bar', 'xn--fsq', 'é', 'x'.repeat(64)]) assert.throws(() => loadConfig({ ...base, POSTMAN_WORKSPACE_SUBDOMAIN: value }), /POSTMAN_WORKSPACE_SUBDOMAIN/);
47
- });
48
-
49
- test('pure builder returns exact allowlisted URL/init and keeps facade/token headers separate', () => {
50
- const raw = buildWebSessionTextRequest(normalized(), config());
51
- assert.equal(raw.url.href, 'https://team-7.postman.co/_gw/chat');
52
- assert.equal(raw.init.method, 'POST');
53
- assert.equal(raw.init.redirect, 'error');
54
- assert.deepEqual(Object.keys(raw.init.headers).sort(), ['accept', 'content-type', 'cookie', 'origin', 'referer', 'user-agent', 'x-pstmn-req-service'].sort());
55
- assert.equal(raw.init.headers.cookie, 'postman.sid=session-test-dummy');
56
- assert.equal(raw.init.headers.origin, 'https://team-7.postman.co');
57
- assert.equal(raw.init.headers.referer, 'https://team-7.postman.co/');
58
- assert.equal(raw.init.headers.authorization, undefined);
59
- assert.equal(raw.init.headers['x-access-token'], undefined);
60
- assert.equal(JSON.stringify(raw).includes('facade-test-dummy'), false);
61
- });
62
-
63
- test('body is deterministic, config-owned, fresh text-only, and does not mutate caller input', () => {
64
- const source = { model: 'public-model', messages: [{ role: 'user', content: 'hello' }], platform: 'ATTACK', workspaceId: 'ATTACK', conversationId: 'ATTACK', cookie: 'ATTACK', origin: 'ATTACK', selectedModel: 'ATTACK', devModeOptions: { selectedModel: 'ATTACK' } };
65
- const before = structuredClone(source);
66
- const c = config();
67
- const request = normalizeOpenAIRequest(source, c);
68
- const a = buildWebSessionTextRequest(request, c);
69
- const b = buildWebSessionTextRequest(request, c);
70
- assert.equal(a.init.body, b.init.body);
71
- assert.deepEqual(source, before);
72
- const body = JSON.parse(a.init.body);
73
- assert.equal(body.platform, 'WEB');
74
- assert.equal(body.mandatoryContext.workspaceId, 'workspace-test-dummy');
75
- assert.equal(body.input.conversationId, null);
76
- assert.equal(body.devModeOptions.selectedModel, 'UPSTREAM_PIN_TEST');
77
- assert.equal(body.devModeOptions.isParallelToolCallingSupported, false);
78
- assert.deepEqual(body.clientTools, { native: [] });
79
- assert.equal(a.init.body.includes('ATTACK'), false);
80
- const query = JSON.parse(body.input.query);
81
- assert.equal(query.format, 'openai-full-history-v1');
82
- assert.deepEqual(query.messages, [{ role: 'user', content: 'hello' }]);
83
- });
84
-
85
- test('web-session builder advertises tools, preserves assistant tool history, and rejects uncorrelated results and named tool choice', () => {
86
- const c = config();
87
- const tool = { type: 'function', function: { name: 'lookup', parameters: { type: 'object', properties: {} } } };
88
- const built=buildWebSessionTextRequest(normalizeOpenAIRequest({ model: 'public-model', messages: [{ role: 'user', content: 'x' }], tools: [tool] }, c), c);
89
- assert.deepEqual(JSON.parse(built.init.body).clientTools.thirdParty['proxy-tools'].tools.map(value=>value.name), ['openai__lookup']);
90
- const fake = { ...normalized(), messages: [{ role: 'assistant', content: '', tool_calls: [{ id: 'x' }] }], toolChoice: 'none' };
91
- const historyBody=JSON.parse(buildWebSessionTextRequest(fake, c).init.body);
92
- const historyQuery=JSON.parse(historyBody.input.query);
93
- assert.equal(historyQuery.messages[0].role,'assistant');
94
- assert.equal(historyQuery.messages[0].tool_calls[0].id,'x');
95
- assert.throws(() => buildWebSessionTextRequest({ ...normalized(), toolResults: [{ toolCallId: 'x', content: 'y' }] }, c), error=>error.code==='unknown_tool_call');
96
- assert.throws(() => buildWebSessionTextRequest({ ...normalized(), toolChoice: {type:'function',name:'lookup'} }, c), /named tool_choice/);
97
- });
98
-
99
- test('serialized body obeys MAX_BODY_BYTES and optional app version is validated/allowlisted', () => {
100
- const c = loadConfig({ ...base, MAX_BODY_BYTES: '1024', POSTMAN_APP_VERSION: 'app-test-1' });
101
- const raw = buildWebSessionTextRequest(normalizeOpenAIRequest({ model: 'public-model', messages: [{ role: 'user', content: 'ok' }] }, c), c);
102
- assert.equal(raw.init.headers['x-app-version'], 'app-test-1');
103
- assert.throws(() => buildWebSessionTextRequest(normalizeOpenAIRequest({ model: 'public-model', messages: [{ role: 'user', content: 'x'.repeat(2000) }] }, c), c), error => error.code === 'request_too_large' && error.status === 413);
104
- assert.throws(() => loadConfig({ ...base, POSTMAN_APP_VERSION: 'bad\r\nheader' }), /POSTMAN_APP_VERSION/);
105
- });
106
-
107
- test('existing live request dispatcher rejects web_session before fetch/event parsing', () => {
108
- assert.throws(() => buildPostmanRequest(normalized(), config()), error => error.code === 'web_session_transport_not_active' && error.status === 400);
109
- });
110
-
111
- test('provider server activates web_session only when the explicit strategy is configured', async () => {
112
- const c = { ...config(), timeoutMs: 1000, maxConcurrent: 1, correlationPendingTtlMs: 1000, correlationTerminalTtlMs: 1000, correlationInflightTtlMs: 1000, correlationMaxGroups: 10, correlationMaxCalls: 10, correlationMaxBytes: 4096, maxEventBytes: 4096, maxOutputBytes: 4096, maxToolArgumentBytes: 1024, maxToolResultBytes: 1024, exposeHealthDetails: false };
113
- let fetchCalls = 0;
114
- const server = createProviderServer(c, { fetchImpl: async () => { fetchCalls++; return new Response('data: {"eventType":"textChunk","data":{"textContent":"active"}}\n\ndata: [DONE]\n\n', { status: 200, headers: { 'content-type': 'text/event-stream' } }); }, logger: { error() {} } });
115
- const baseUrl = await listen(server);
116
- try {
117
- const response = await request(baseUrl, '/v1/chat/completions', { method: 'POST', token: 'facade-test-dummy', body: { model: 'public-model', messages: [{ role: 'user', content: 'hello' }] } });
118
- assert.equal(response.status, 200);
119
- assert.equal((await response.json()).choices[0].message.content, 'active');
120
- assert.equal(fetchCalls, 1);
121
- } finally {
122
- await new Promise(resolve => server.close(resolve));
123
- }
124
- });
@@ -1,241 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { createWebSessionEventParser } from '../src/web-session-events.mjs';
4
-
5
- const encoder = new TextEncoder();
6
- const bytes = value => encoder.encode(value);
7
- const jsonFrame = (value, options = {}) => {
8
- const payload = JSON.stringify(value);
9
- const lines = options.splitDataAt === undefined
10
- ? [`data: ${payload}`]
11
- : [`data: ${payload.slice(0, options.splitDataAt)}`, `data:${payload.slice(options.splitDataAt)}`];
12
- return `${options.event ? `event: ${options.event}\n` : ''}${lines.join('\n')}\n\n`;
13
- };
14
- const make = options => createWebSessionEventParser({ maxEventBytes: 4096, maxOutputBytes: 4096, maxObservationBytes: 256, maxEvents: 32, requestedModel: 'public-model', selectedUpstreamModel: 'SELECTED_PIN', allowedObservedModels: ['OBSERVED_PIN'], ...options });
15
- const feedPieces = (parser, source, cuts) => {
16
- const encoded = bytes(source);
17
- let start = 0;
18
- for (const end of cuts) { parser.feed(encoded.slice(start, end)); start = end; }
19
- if (start < encoded.length) parser.feed(encoded.slice(start));
20
- return parser.end();
21
- };
22
- const throwsCode = (fn, code) => assert.throws(fn, error => error?.code === code && !String(error?.upstreamDetail?.message ?? '').includes('secret-test-value'));
23
-
24
- test('fragmented UTF-8, CRLF boundaries, source-backed progress, observations, and explicit DONE succeed', () => {
25
- const parser = make();
26
- const source = [
27
- jsonFrame({ eventType: 'conversation', data: { id: 'conversation-1' } }).replaceAll('\n', '\r\n'),
28
- jsonFrame({ eventType: 'planningChunk', data: {} }),
29
- jsonFrame({ eventType: 'progressUpdate', data: {} }),
30
- jsonFrame({ eventType: 'todoChunk', data: {} }),
31
- jsonFrame({ eventType: 'streamingFormat', data: {} }),
32
- jsonFrame({ eventType: 'thinkingComplete', data: {} }),
33
- jsonFrame({ eventType: 'usage', data: { usageState: 'AVAILABLE', inputTokens: 2, outputTokens: 3, totalTokens: 5 } }),
34
- jsonFrame({ eventType: 'textChunk', data: { textContent: 'héllo 🌍', metadata: { model: 'OBSERVED_PIN' } } }),
35
- 'data: [DONE]\n\n',
36
- ].join('');
37
- const encoded = bytes(source);
38
- const emoji = bytes('🌍');
39
- const emojiAt = Buffer.from(encoded).indexOf(Buffer.from(emoji));
40
- const result = feedPieces(parser, source, [1, 7, 19, emojiAt + 1, emojiAt + 3, encoded.length - 2]);
41
- assert.equal(result.outcome, 'complete_explicit');
42
- assert.equal(result.successful, true);
43
- assert.equal(result.text, 'héllo 🌍');
44
- assert.equal(result.error, null);
45
- assert.deepEqual(result.observations.usage, { prompt_tokens: 2, completion_tokens: 3, total_tokens: 5, estimated: false });
46
- assert.equal(result.observations.conversationId, 'conversation-1');
47
- assert.equal(result.observations.requestedModel, 'public-model');
48
- assert.equal(result.observations.selectedUpstreamModel, 'SELECTED_PIN');
49
- assert.equal(result.observations.observedUpstreamModel, 'OBSERVED_PIN');
50
- assert.equal(result.observations.modelVerification, 'matched');
51
- assert.equal(result.observations.parserBufferComplete, true);
52
- assert.deepEqual(result.observations.eventTypes, ['conversation', 'planningChunk', 'progressUpdate', 'todoChunk', 'streamingFormat', 'thinkingComplete', 'usage', 'textChunk']);
53
- });
54
-
55
- test('multiple data lines form one JSON payload and an absent model observation stays unobserved', () => {
56
- const event = { eventType: 'textChunk', data: { textContent: 'split' } };
57
- const frame = jsonFrame(event, { splitDataAt: JSON.stringify(event).indexOf(',') + 1 });
58
- const parser = make();
59
- parser.feed(bytes(frame));
60
- parser.feed(bytes('data: [DONE]\n\n'));
61
- const result = parser.end();
62
- assert.equal(result.text, 'split');
63
- assert.equal(result.successful, true);
64
- assert.equal(result.observations.observedUpstreamModel, null);
65
- assert.equal(result.observations.modelVerification, 'unobserved');
66
- });
67
-
68
- test('EOF after terminated text frame is explicitly ambiguous and exposes text without successful final', () => {
69
- const parser = make();
70
- parser.feed(bytes(jsonFrame({ eventType: 'textChunk', data: { textContent: 'partial answer' } })));
71
- const result = parser.end();
72
- assert.equal(result.outcome, 'ambiguous_eof');
73
- assert.equal(result.successful, false);
74
- assert.equal(result.text, 'partial answer');
75
- assert.equal(result.error.code, 'upstream_completion_ambiguous');
76
- assert.equal(result.observations.parserBufferComplete, true);
77
- });
78
-
79
- test('partial final line, partial frame, and partial UTF-8 EOF never silently flush or succeed', () => {
80
- const partialLine = make();
81
- partialLine.feed(bytes('data: {"eventType":"textChunk","data":{"textContent":"x"}}'));
82
- let result = partialLine.end();
83
- assert.equal(result.outcome, 'incomplete_truncated');
84
- assert.equal(result.text, '');
85
- assert.equal(result.error.code, 'upstream_incomplete_stream');
86
-
87
- const partialFrame = make();
88
- partialFrame.feed(bytes('data: {"eventType":"textChunk","data":{"textContent":"x"}}\n'));
89
- result = partialFrame.end();
90
- assert.equal(result.outcome, 'incomplete_truncated');
91
- assert.equal(result.text, '');
92
- assert.equal(result.error.code, 'upstream_incomplete_stream');
93
-
94
- const partialUtf8 = make();
95
- const encoded = bytes(jsonFrame({ eventType: 'textChunk', data: { textContent: '🌍' } }));
96
- const emojiAt = Buffer.from(encoded).indexOf(Buffer.from(bytes('🌍')));
97
- partialUtf8.feed(encoded.slice(0, emojiAt + 2));
98
- result = partialUtf8.end();
99
- assert.equal(result.outcome, 'incomplete_truncated');
100
- assert.equal(result.successful, false);
101
- assert.equal(result.error.code, 'upstream_invalid_utf8');
102
- });
103
-
104
- test('empty/metadata-only EOF is incomplete and exact DONE may complete an otherwise empty stream', () => {
105
- let parser = make();
106
- assert.equal(parser.end().outcome, 'incomplete_empty');
107
- parser = make();
108
- parser.feed(bytes(jsonFrame({ eventType: 'ping', data: {} })));
109
- assert.equal(parser.end().outcome, 'incomplete_empty');
110
- parser = make();
111
- parser.feed(bytes('data: [DONE]\n\n'));
112
- const result = parser.end();
113
- assert.equal(result.outcome, 'complete_explicit');
114
- assert.equal(result.successful, true);
115
- assert.equal(result.text, '');
116
- });
117
-
118
- test('captures conversation identifiers repeated on non-conversation chunks', () => {
119
- const parser = make();
120
- parser.feed(bytes(jsonFrame({ eventType: 'textChunk', data: { textContent: 'ok', metadata: { conversationId: 'conversation-late' } } })));
121
- parser.feed(bytes('data: [DONE]\n\n'));
122
- const result = parser.end();
123
- assert.equal(result.observations.conversationId, 'conversation-late');
124
- });
125
-
126
- test('unknown events, malformed tool fragments, loop approval, malformed JSON, invalid event values, and invalid terminal forms fail closed', () => {
127
- const cases = [
128
- [jsonFrame({ eventType: 'futureChunk', data: {} }), 'upstream_unverified_event_shape'],
129
- [jsonFrame({ eventType: 'toolCallChunk', data: { secret: 'secret-test-value' } }), 'upstream_tool_fragment_error'],
130
- [jsonFrame({ eventType: 'loopApprovalChunk', data: { message: 'secret-test-value' } }), 'upstream_web_session_loop_approval_unsupported'],
131
- ['data: {bad json secret-test-value}\n\n', 'upstream_invalid_json'],
132
- ['data: []\n\n', 'upstream_invalid_event'],
133
- ['event: completion\ndata: [DONE]\n\n', 'upstream_invalid_terminal'],
134
- ];
135
- for (const [source, code] of cases) {
136
- const parser = make();
137
- throwsCode(() => parser.feed(bytes(source)), code);
138
- const result = parser.end();
139
- assert.equal(result.successful, false);
140
- assert.equal(result.error.code, code);
141
- assert.equal(JSON.stringify(result).includes('secret-test-value'), false);
142
- }
143
- });
144
-
145
- test('failure and credit states after text preserve bounded text but never become successful', () => {
146
- for (const [event, code] of [
147
- [{ eventType: 'failure', data: { message: 'secret-test-value' } }, 'upstream_gateway_failure'],
148
- [{ eventType: 'error', data: { error: 'secret-test-value' } }, 'upstream_gateway_failure'],
149
- [{ eventType: 'usage', data: { usageState: 'BLOCKED' } }, 'upstream_credit_blocked'],
150
- [{ eventType: 'usage', data: { usageState: 'MYSTERY' } }, 'upstream_unknown_credit_state'],
151
- ]) {
152
- const parser = make();
153
- parser.feed(bytes(jsonFrame({ eventType: 'textChunk', data: { textContent: 'visible' } })));
154
- throwsCode(() => parser.feed(bytes(jsonFrame(event))), code);
155
- const result = parser.end();
156
- assert.equal(result.text, 'visible');
157
- assert.equal(result.successful, false);
158
- assert.equal(result.error.code, code);
159
- assert.equal(JSON.stringify(result).includes('secret-test-value'), false);
160
- }
161
- });
162
-
163
- test('model requested/selected/observed remain distinct; missing mapping is unverified and exact configured mismatch fails', () => {
164
- let parser = make({ allowedObservedModels: [] });
165
- parser.feed(bytes(jsonFrame({ eventType: 'textChunk', data: { textContent: 'x', metadata: { model: 'UNMAPPED_OBSERVATION' } } })));
166
- parser.feed(bytes('data: [DONE]\n\n'));
167
- let result = parser.end();
168
- assert.equal(result.successful, true);
169
- assert.equal(result.observations.modelVerification, 'unverified');
170
- assert.equal(result.observations.requestedModel, 'public-model');
171
- assert.equal(result.observations.selectedUpstreamModel, 'SELECTED_PIN');
172
- assert.equal(result.observations.observedUpstreamModel, 'UNMAPPED_OBSERVATION');
173
-
174
- parser = make();
175
- throwsCode(() => parser.feed(bytes(jsonFrame({ eventType: 'textChunk', data: { textContent: 'x', metadata: { model: 'WRONG' } } }))), 'upstream_model_mismatch');
176
- result = parser.end();
177
- assert.equal(result.successful, false);
178
- assert.equal(result.text, '');
179
- assert.equal(result.observations.modelVerification, 'mismatched');
180
-
181
- parser = make();
182
- parser.feed(bytes(jsonFrame({ eventType: 'textChunk', data: { textContent: 'visible', metadata: { model: 'OBSERVED_PIN' } } })));
183
- throwsCode(() => parser.feed(bytes(jsonFrame({ eventType: 'info', data: { metadata: { model: 'WRONG' } } }))), 'upstream_model_mismatch');
184
- result = parser.end();
185
- assert.equal(result.text, 'visible');
186
- assert.equal(result.successful, false);
187
- });
188
-
189
- test('event, output, observation, and parser-buffer bounds fail without silent truncation', () => {
190
- let parser = make({ maxOutputBytes: 4 });
191
- throwsCode(() => parser.feed(bytes(jsonFrame({ eventType: 'textChunk', data: { textContent: '12345' } }))), 'upstream_output_too_large');
192
- assert.equal(parser.end().text, '');
193
-
194
- parser = make({ maxObservationBytes: 4 });
195
- throwsCode(() => parser.feed(bytes(jsonFrame({ eventType: 'conversation', data: { id: '12345' } }))), 'upstream_invalid_conversation');
196
-
197
- parser = make({ maxEvents: 1 });
198
- parser.feed(bytes(jsonFrame({ eventType: 'ping', data: {} })));
199
- throwsCode(() => parser.feed(bytes(jsonFrame({ eventType: 'info', data: {} }))), 'upstream_event_count_exceeded');
200
-
201
- parser = make({ maxEventBytes: 64 });
202
- throwsCode(() => parser.feed(bytes(`data: ${'x'.repeat(80)}\n\n`)), 'upstream_event_too_large');
203
- });
204
-
205
- test('data after terminal, invalid bytes, wrong chunk type, and feed after end are rejected', () => {
206
- let parser = make();
207
- parser.feed(bytes('data: [DONE]\n\n'));
208
- throwsCode(() => parser.feed(bytes(jsonFrame({ eventType: 'ping', data: {} }))), 'upstream_data_after_terminal');
209
-
210
- parser = make();
211
- parser.feed(bytes('data: [DONE]\n\ntrailing-secret-test-value'));
212
- let result = parser.end();
213
- assert.equal(result.successful, false);
214
- assert.equal(result.error.code, 'upstream_data_after_terminal');
215
- assert.equal(JSON.stringify(result).includes('secret-test-value'), false);
216
-
217
- parser = make();
218
- parser.feed(bytes('data: [DONE]\n\n'));
219
- throwsCode(() => parser.feed(bytes('data: [DONE]\n\n')), 'upstream_data_after_terminal');
220
-
221
- parser = make();
222
- throwsCode(() => parser.feed(new Uint8Array([0xff])), 'upstream_invalid_utf8');
223
-
224
- parser = make();
225
- assert.throws(() => parser.feed('data: [DONE]\n\n'), /Uint8Array/);
226
- parser.feed(bytes('data: [DONE]\n\n'));
227
- parser.end();
228
- assert.throws(() => parser.feed(bytes('')), /after end/);
229
- });
230
-
231
- test('502 upstream_model_mismatch carries observedModel on the error object', () => {
232
- const parser = make();
233
- try {
234
- parser.feed(bytes(jsonFrame({ eventType: 'textChunk', data: { textContent: 'x', metadata: { model: 'REAL_WIRELABEL_CLAUDE_TEST' } } })));
235
- assert.fail('should have thrown');
236
- } catch (err) {
237
- assert.equal(err.code, 'upstream_model_mismatch');
238
- assert.equal(err.observedModel, 'REAL_WIRELABEL_CLAUDE_TEST');
239
- assert.equal(err.observedModelField, 'data.metadata.model');
240
- }
241
- });
@@ -1,103 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { createProviderServer } from '../src/server.mjs';
4
- import { loadConfig } from '../src/config.mjs';
5
- import { listen, request, fixture } from './helpers.mjs';
6
-
7
- const env = {
8
- PROVIDER_API_KEY: 'facade-dummy-key', PROVIDER_MODELS: 'public-model',
9
- POSTMAN_TRANSPORT_STRATEGY: 'web_session', POSTMAN_SESSION_COOKIE: 'dummy-cookie-value',
10
- POSTMAN_WORKSPACE_SUBDOMAIN: 'fixture-team', POSTMAN_WORKSPACE_ID: 'fixture-workspace',
11
- POSTMAN_WEB_SESSION_SELECTED_MODEL: 'GPT_56_SOL', POSTMAN_WEB_SESSION_OBSERVED_MODELS: 'gpt-5.6-sol',
12
- REQUEST_TIMEOUT_MS: '1000', MAX_BODY_BYTES: '8192', MAX_EVENT_BYTES: '8192', MAX_OUTPUT_BYTES: '8192',
13
- MAX_TOOL_ARGUMENT_BYTES: '1024', MAX_TOOL_RESULT_BYTES: '1024', MAX_TOOLS: '8', MAX_CONCURRENT_REQUESTS: '2',
14
- };
15
- const encoder = new TextEncoder();
16
- const response = ({ events = [], done = true, status = 200, contentType = 'text/event-stream', suffix = '' }) => {
17
- const wire = events.map(event => `data: ${JSON.stringify(event)}\n\n`).join('') + (done ? 'data: [DONE]\n\n' : '') + suffix;
18
- return new Response(new ReadableStream({ start(controller) { controller.enqueue(encoder.encode(wire)); controller.close(); } }), { status, headers: { 'content-type': contentType } });
19
- };
20
- async function withServer(fetchImpl, fn, override = {}) {
21
- const server = createProviderServer({ ...loadConfig(env), ...override }, { fetchImpl, logger: { error() {} } });
22
- const base = await listen(server);
23
- try { await fn(base); } finally { await new Promise(resolve => server.close(resolve)); }
24
- }
25
- const body = stream => ({ model: 'public-model', messages: [{ role: 'user', content: 'Reply exactly.' }], stream });
26
-
27
- test('explicit web_session returns OpenAI JSON from source-derived observed-DONE fixture and isolates auth headers', async () => {
28
- const source = await fixture('web-session-observed-done');
29
- let capture;
30
- await withServer(async (url, init) => { capture = { url: String(url), init, requestBody: JSON.parse(init.body) }; return response(source); }, async base => {
31
- const result = await request(base, '/v1/chat/completions', { method: 'POST', token: 'facade-dummy-key', body: body(false) });
32
- assert.equal(result.status, 200);
33
- const value = await result.json();
34
- assert.equal(value.choices[0].message.content, 'PROXY_OBSERVATION_OK');
35
- assert.equal(value.choices[0].finish_reason, 'stop');
36
- assert.equal(capture.url, 'https://fixture-team.postman.co/_gw/chat');
37
- assert.equal(capture.init.headers.cookie, 'postman.sid=dummy-cookie-value');
38
- assert.equal(capture.init.headers.authorization, undefined);
39
- assert.equal(capture.init.headers['x-access-token'], undefined);
40
- assert.equal(JSON.stringify(capture).includes('facade-dummy-key'), false);
41
- assert.equal(capture.requestBody.devModeOptions.selectedModel, 'GPT_56_SOL');
42
- });
43
- });
44
-
45
- test('explicit web_session returns OpenAI SSE with content, stop, and DONE only after proven upstream DONE', async () => {
46
- const source = await fixture('web-session-observed-done');
47
- await withServer(() => response(source), async base => {
48
- const result = await request(base, '/v1/chat/completions', { method: 'POST', token: 'facade-dummy-key', body: body(true) });
49
- assert.match(result.headers.get('content-type'), /text\/event-stream/);
50
- const wire = await result.text();
51
- assert.match(wire, /"content":"PROXY_OBSERVATION_OK"/);
52
- assert.match(wire, /"finish_reason":"stop"/);
53
- assert.match(wire, /data: \[DONE\]/);
54
- });
55
- });
56
-
57
- test('missing model observation is allowed but configured observed mismatch fails before successful response', async () => {
58
- await withServer(() => response({ events: [{ eventType: 'textChunk', data: { textContent: 'unobserved' } }] }), async base => {
59
- const result = await request(base, '/v1/chat/completions', { method: 'POST', token: 'facade-dummy-key', body: body(false) });
60
- assert.equal(result.status, 200); assert.equal((await result.json()).choices[0].message.content, 'unobserved');
61
- });
62
- await withServer(() => response({ events: [{ eventType: 'textChunk', data: { textContent: 'wrong', metadata: { model: 'other-model' } } }] }), async base => {
63
- const result = await request(base, '/v1/chat/completions', { method: 'POST', token: 'facade-dummy-key', body: body(false) });
64
- assert.equal(result.status, 502); assert.equal((await result.json()).error.code, 'upstream_model_mismatch');
65
- });
66
- });
67
-
68
- test('error event, ambiguous EOF, wrong content type, and HTTP failure never become successful web_session completions', async () => {
69
- const cases = [
70
- [() => response({ events: [{ eventType: 'textChunk', data: { textContent: 'partial' } }, { eventType: 'error', data: { error: 'secret-upstream-value' } }] }), 502, 'upstream_gateway_failure'],
71
- [() => response({ events: [{ eventType: 'textChunk', data: { textContent: 'partial' } }], done: false }), 502, 'upstream_completion_ambiguous'],
72
- [() => response({ events: [], contentType: 'application/json' }), 502, 'upstream_content_type_error'],
73
- [() => response({ events: [], status: 401 }), 401, 'upstream_http_error'],
74
- [() => response({ events: [], status: 503 }), 503, 'upstream_http_error'],
75
- ];
76
- for (const [upstream, status, code] of cases) await withServer(upstream, async base => {
77
- const result = await request(base, '/v1/chat/completions', { method: 'POST', token: 'facade-dummy-key', body: body(false) });
78
- assert.equal(result.status, status); const wire = await result.text(); assert.equal(JSON.parse(wire).error.code, code); assert.equal(wire.includes('secret-upstream-value'), false);
79
- });
80
- });
81
-
82
- test('web_session accepts declared tools while preserving ingress auth failure isolation', async () => {
83
- let calls = 0; let capture;
84
- await withServer(async (_url, init) => { calls++; capture=JSON.parse(init.body); return response({ events: [] }); }, async base => {
85
- const toolBody = { ...body(false), tools: [{ type: 'function', function: { name: 'lookup', parameters: { type: 'object', properties: {} } } }] };
86
- let result = await request(base, '/v1/chat/completions', { method: 'POST', token: 'facade-dummy-key', body: toolBody });
87
- assert.equal(result.status, 200); assert.deepEqual(capture.clientTools.thirdParty['proxy-tools'].tools.map(tool=>tool.name), ['openai__lookup']); assert.equal(calls, 1);
88
- result = await request(base, '/v1/chat/completions', { method: 'POST', token: 'wrong-key', body: body(false) });
89
- assert.equal(result.status, 401); assert.equal((await result.json()).error.code, 'invalid_api_key'); assert.equal(calls, 1);
90
- });
91
- });
92
-
93
- test('web_session propagates client abort to injected upstream', async () => {
94
- let upstreamAborted = false;
95
- await withServer((url, init) => new Promise((resolve, reject) => init.signal.addEventListener('abort', () => { upstreamAborted = true; reject(init.signal.reason); }, { once: true })), async base => {
96
- const controller = new AbortController();
97
- const pending = request(base, '/v1/chat/completions', { method: 'POST', token: 'facade-dummy-key', body: body(false), signal: controller.signal });
98
- setTimeout(() => controller.abort(new DOMException('fixture abort', 'AbortError')), 20);
99
- await assert.rejects(pending, error => error?.name === 'AbortError');
100
- await new Promise(resolve => setTimeout(resolve, 20));
101
- assert.equal(upstreamAborted, true);
102
- });
103
- });
@@ -1,52 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { createProviderServer } from '../src/server.mjs';
4
- import { loadConfig } from '../src/config.mjs';
5
- import { CorrelationStore } from '../src/correlation-store.mjs';
6
- import { listen, request, fixture } from './helpers.mjs';
7
-
8
- const env={PROVIDER_API_KEY:'key',PROVIDER_MODELS:'m',POSTMAN_TRANSPORT_STRATEGY:'web_session',POSTMAN_SESSION_COOKIE:'dummy',POSTMAN_WORKSPACE_SUBDOMAIN:'team',POSTMAN_WORKSPACE_ID:'ws',POSTMAN_WEB_SESSION_SELECTED_MODEL:'GPT',REQUEST_TIMEOUT_MS:'1000',MAX_BODY_BYTES:'65536',MAX_EVENT_BYTES:'65536',MAX_OUTPUT_BYTES:'65536',MAX_TOOL_ARGUMENT_BYTES:'4096',MAX_TOOL_RESULT_BYTES:'4096',MAX_TOOLS:'8',MAX_CONCURRENT_REQUESTS:'4'};
9
- const config=loadConfig(env); const encoder=new TextEncoder();
10
- const sse=(events,{done=true}={})=>new Response(new ReadableStream({start(c){for(const e of events)c.enqueue(encoder.encode(`data: ${JSON.stringify(e)}\n\n`));if(done)c.enqueue(encoder.encode('data: [DONE]\n\n'));c.close();}}),{status:200,headers:{'content-type':'text/event-stream'}});
11
- const tools=[
12
- {type:'function',function:{name:'lookup',description:'Lookup',parameters:{type:'object',properties:{q:{type:'string'}},required:['q'],additionalProperties:false}}},
13
- {type:'function',function:{name:'count',description:'Count',parameters:{type:'object',properties:{n:{type:'integer'}},required:['n'],additionalProperties:false}}},
14
- ];
15
- const body=(messages= [{role:'user',content:'use tools'}],more={})=>({model:'m',messages,tools,stream:false,...more});
16
- async function withServer(fetchImpl,fn,override={}){const server=createProviderServer({...config,...override},{fetchImpl,logger:{error(){}}});const base=await listen(server);try{await fn(base);}finally{await new Promise(r=>server.close(r));}}
17
-
18
- test('web_session advertises minimal catalog, buffers grouped fragments, continues with correlated TOOL_RESPONSE, and rejects replay',async()=>{
19
- const source=await fixture('web-session-tool-fragments'); let turn=0; const captures=[];
20
- await withServer(async(url,init)=>{captures.push(JSON.parse(init.body));return turn++===0?sse(source.events):sse([{eventType:'conversation',data:{id:'conv_source_fixture'}},{eventType:'textChunk',data:{textContent:'final'}}]);},async base=>{
21
- const first=await request(base,'/v1/chat/completions',{method:'POST',token:'key',body:body()}); assert.equal(first.status,200); const v=await first.json(); assert.equal(v.choices[0].finish_reason,'tool_calls'); const assistant=v.choices[0].message; assert.equal(assistant.tool_calls.length,2); assert.match(assistant.tool_calls[0].id,/^call_pmn_/); assert.deepEqual(JSON.parse(assistant.tool_calls[0].function.arguments),{q:'x'}); assert.deepEqual(JSON.parse(assistant.tool_calls[1].function.arguments),{n:2});
22
- assert.deepEqual(captures[0].clientTools.thirdParty['proxy-tools'].tools.map(t=>t.name),['openai__lookup','openai__count']); assert.equal(captures[0].devModeOptions.autoRun,true);
23
- const continuation=body([{role:'user',content:'use tools'},assistant,{role:'tool',tool_call_id:assistant.tool_calls[0].id,content:'A'},{role:'tool',tool_call_id:assistant.tool_calls[1].id,content:'B'}]);
24
- const second=await request(base,'/v1/chat/completions',{method:'POST',token:'key',body:continuation}); assert.equal(second.status,200); assert.equal((await second.json()).choices[0].message.content,'final'); assert.equal(captures[1].input.chatType,'TOOL_RESPONSE'); assert.equal(captures[1].input.toolCallGroupId,'up_group_1'); assert.equal(captures[1].input.toolCallId,'up_call_a'); assert.deepEqual(JSON.parse(captures[1].input.toolResponse).map(x=>x.toolCallId),['up_call_a','up_call_b']);
25
- const replay=await request(base,'/v1/chat/completions',{method:'POST',token:'key',body:continuation}); assert.equal(replay.status,409); assert.equal((await replay.json()).error.code,'tool_call_already_consumed'); assert.equal(captures.length,2);
26
- });
27
- });
28
-
29
- test('AICoworker-style continuation preserves role=tool correlation and dispatches exactly once',async()=>{
30
- const source=await fixture('web-session-tool-fragments'); let turn=0; const captures=[];
31
- await withServer(async(url,init)=>{captures.push(JSON.parse(init.body));return turn++===0?sse(source.events):sse([{eventType:'conversation',data:{id:'conv_source_fixture'}},{eventType:'textChunk',data:{textContent:'AKI_ONCE'}}]);},async base=>{
32
- const first=await request(base,'/v1/chat/completions',{method:'POST',token:'key',body:body()}); const assistant=(await first.json()).choices[0].message;
33
- const continuation=body([{role:'user',content:'use tools'},assistant,{role:'tool',tool_call_id:assistant.tool_calls[0].id,content:[{type:'text',text:'A'}]},{role:'tool',tool_call_id:assistant.tool_calls[1].id,content:[{type:'text',text:'B'}]}]);
34
- const second=await request(base,'/v1/chat/completions',{method:'POST',token:'key',body:continuation}); assert.equal(second.status,200); assert.equal((await second.json()).choices[0].message.content,'AKI_ONCE');
35
- assert.equal(captures.length,2); assert.equal(captures[1].input.chatType,'TOOL_RESPONSE'); assert.equal(captures[1].input.toolCallId,'up_call_a'); assert.deepEqual(JSON.parse(captures[1].input.toolResponse).map(x=>x.content),['A','B']);
36
- });
37
- });
38
-
39
- test('web_session rejects bad arguments, partial group, loop approval, and ambiguous EOF before admission',async()=>{
40
- const cases=[
41
- [[{eventType:'conversation',data:{id:'c'}},{eventType:'toolCallChunk',data:{id:'a',index:0,name:'openai__count',arguments:'{\"n\":\"bad\"}'}}],{},'upstream_tool_schema_mismatch'],
42
- [[{eventType:'conversation',data:{id:'c'}},{eventType:'toolCallChunk',data:{toolCalls:[{id:'a',index:0,name:'openai__lookup',toolCallGroupId:'g',arguments:'{\"q\":\"x\"}'},{id:'b',index:1,name:'openai__count',arguments:'{\"n\":2}' }]}}],{},'upstream_ungrouped_tool_batch'],
43
- [[{eventType:'loopApprovalChunk',data:{message:'approve'}}],{},'upstream_web_session_loop_approval_unsupported'],
44
- [[{eventType:'conversation',data:{id:'c'}},{eventType:'toolCallChunk',data:{id:'a',index:0,name:'openai__lookup',arguments:'{\"q\":\"x\"}'}}],{done:false},'upstream_completion_ambiguous'],
45
- ];
46
- for(const [events,opts,code] of cases){let calls=0;await withServer(()=>{calls++;return sse(events,opts);},async base=>{const r=await request(base,'/v1/chat/completions',{method:'POST',token:'key',body:body()});const expectedStatus=code==='upstream_web_session_loop_approval_unsupported'?400:502;assert.equal(r.status,expectedStatus);assert.equal((await r.json()).error.code,code);assert.equal(calls,1);});}
47
- });
48
-
49
- test('correlation binds principal, strategy and origin without mutating pending record',()=>{
50
- const store=new CorrelationStore(); const common={principalId:'p',workspaceId:'ws',model:'m',transportStrategy:'web_session',transportOrigin:'https://team.postman.co',conversationId:'c',transcript:[{role:'user',content:'u'},{role:'assistant',content:'',tool_calls:[{id:'up',type:'function',function:{name:'lookup',arguments:'{\"q\":\"x\"}'}}]}],toolsBinding:{tools:[tools[0]],toolChoice:'auto'},catalogMetadata:{strategy:'web_session'},calls:[{upstreamToolCallId:'up',upstreamGroupId:null,originalName:'lookup',argumentsJson:'{\"q\":\"x\"}',schemaHash:'s'}]}; const g=store.admit(common); const result=[{toolCallId:g.calls[0].publicToolCallId,content:'ok'}]; const claim={principalId:'p',results:result,workspaceId:'ws',model:'m',transportStrategy:'web_session',transportOrigin:'https://team.postman.co',transcript:g.transcript,toolsBinding:common.toolsBinding,catalogMetadata:common.catalogMetadata};
51
- assert.throws(()=>store.claim({...claim,transportStrategy:'access_token'}),e=>e.code==='continuation_strategy_mismatch'); assert.equal(g.state,'PENDING'); assert.throws(()=>store.claim({...claim,transportOrigin:'https://other.postman.co'}),e=>e.code==='continuation_origin_mismatch'); assert.equal(g.state,'PENDING'); assert.throws(()=>store.claim({...claim,principalId:'other'}),e=>e.code==='unknown_tool_call');
52
- });