@newheap/platform-ai-chat 0.21.0 → 0.22.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.
@@ -65,7 +65,8 @@ const statusTable = {
65
65
  'assistant-forbidden',
66
66
  'assistant-mcp-credential-mismatch',
67
67
  'assistant-mcp-initiator-only',
68
- 'assistant-file-forbidden'
68
+ 'assistant-file-forbidden',
69
+ 'assistant-not-allowed-while-impersonating'
69
70
  ],
70
71
  404: [
71
72
  'assistant-disabled',
@@ -1687,6 +1688,10 @@ class NhAssistantMockAdminMcp {
1687
1688
  stored.organization?.status === 'issuer-changed') {
1688
1689
  stored.organization = { ...stored.organization, status: 'active' };
1689
1690
  }
1691
+ if (!keepsSecret(before, input)) {
1692
+ // As on the server: a stored secret never follows the server to another destination.
1693
+ stored.secret = null;
1694
+ }
1690
1695
  if (!secretModes.includes(input.authMode)) {
1691
1696
  stored.secret = null;
1692
1697
  stored.server.secretReference = null;
@@ -2247,17 +2252,20 @@ class NhAssistantMockAdminMcp {
2247
2252
  if (input.secret && input.secretReference) {
2248
2253
  add('secretReference', 'invalid');
2249
2254
  }
2255
+ // As on the server: a stored secret and reference serve only the destination they were given for.
2256
+ const keeps = keepsSecret(stored?.server, input);
2250
2257
  const reference = input.secretReference === undefined || input.secretReference === null
2251
- ? (input.secret ? null : stored?.server.secretReference ?? null)
2258
+ ? (input.secret || !keeps ? null : stored?.server.secretReference ?? null)
2252
2259
  : input.secretReference || null;
2253
2260
  if (reference !== null && (!secretReference.test(reference) || reference.includes('..'))) {
2254
2261
  add('secretReference', 'invalid');
2255
2262
  }
2256
2263
  if (secretModes.includes(input.authMode)) {
2257
2264
  // As on the server: bearer, api-key and basic need a stored secret or a reference. A new stored
2258
- // secret replaces a reference, a new reference a stored secret.
2265
+ // secret replaces a reference, a new reference a stored secret, and another destination needs
2266
+ // either again.
2259
2267
  const storedSecret = input.secret === undefined || input.secret === null
2260
- ? !!stored?.secret && !input.secretReference
2268
+ ? keeps && !!stored?.secret && !input.secretReference
2261
2269
  : input.secret.length > 0;
2262
2270
  if (!storedSecret && (reference === null || !!input.secret)) {
2263
2271
  add('secret', 'required');
@@ -2331,10 +2339,32 @@ class NhAssistantMockAdminMcp {
2331
2339
  };
2332
2340
  }
2333
2341
  }
2342
+ /**
2343
+ * Whether a stored secret and reference stay with the server after the input, as on the server: the
2344
+ * URL keeps its origin (scheme, host and port), and the authentication mode and the `api-key` header,
2345
+ * in any letter case, stay the same.
2346
+ */
2347
+ function keepsSecret(stored, input) {
2348
+ const before = stored ? originOf(stored.url) : null;
2349
+ if (!stored || before === null || stored.authMode !== input.authMode || before !== originOf(input.url)) {
2350
+ return false;
2351
+ }
2352
+ return input.authMode !== 'api-key'
2353
+ || (stored.headerName || 'X-Api-Key').toLowerCase() === (input.headerName || 'X-Api-Key').toLowerCase();
2354
+ }
2355
+ /** The origin of an absolute URL in lowercase, or `null` for an invalid one. */
2356
+ function originOf(url) {
2357
+ try {
2358
+ return new URL(url.trim()).origin.toLowerCase();
2359
+ }
2360
+ catch {
2361
+ return null;
2362
+ }
2363
+ }
2334
2364
  function toServerFields(input, stored) {
2335
2365
  const usesSecret = secretModes.includes(input.authMode);
2336
2366
  const reference = input.secretReference === undefined || input.secretReference === null
2337
- ? (input.secret ? null : stored?.secretReference ?? null)
2367
+ ? (input.secret || !keepsSecret(stored, input) ? null : stored?.secretReference ?? null)
2338
2368
  : input.secretReference || null;
2339
2369
  const certificate = input.clientCertificateReference === undefined || input.clientCertificateReference === null
2340
2370
  ? stored?.clientCertificateReference ?? null
@@ -7224,8 +7254,9 @@ class NhAssistantMockAgentRuns {
7224
7254
  }
7225
7255
  /**
7226
7256
  * `POST runs/{id}/retry`: resumes a failed or cancelled run (`queued`), or restarts the agent in the
7227
- * same thread like a start by hand; needs the approver role on the thread. The mock's resumed
7228
- * attempt ends at once as the scenario's `runNow` says.
7257
+ * same thread like a start by hand; needs the approver role on the thread, and a restart also passes
7258
+ * the start checks of `POST threads/start`. The mock's resumed attempt ends at once as the
7259
+ * scenario's `runNow` says.
7229
7260
  */
7230
7261
  retry(runId, body, language) {
7231
7262
  const mode = body?.mode ?? 'resume';
@@ -7251,7 +7282,17 @@ class NhAssistantMockAgentRuns {
7251
7282
  return mockFailure('assistant-agent-not-found');
7252
7283
  }
7253
7284
  if (mode === 'restart') {
7254
- return this.startNow(agent, found.thread.subject);
7285
+ // A restart starts the agent again for the caller: the rule of a start by hand decides, with the
7286
+ // caller's live role.
7287
+ const subject = this.subjects.describe(found.thread.subject.subjectType, found.thread.subject.subjectId, language, this.context.administers());
7288
+ if (!subject) {
7289
+ return mockFailure('assistant-subject-access-denied');
7290
+ }
7291
+ const refusal = this.refusal(agent, subject);
7292
+ if (refusal) {
7293
+ return mockFailure(refusal);
7294
+ }
7295
+ return this.startNow(agent, subject);
7255
7296
  }
7256
7297
  const runNow = this.scenario.runNow?.[agent.id] ?? {};
7257
7298
  const status = runNow.status ?? 'completed';