@xpert-ai/xpert-sdk 0.17.0 → 0.18.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 (40) hide show
  1. package/dist/client.d.ts +7 -1
  2. package/dist/client.js +27 -2
  3. package/dist/client.js.map +1 -1
  4. package/dist/client.mjs +27 -2
  5. package/dist/client.mjs.map +1 -1
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.js +1 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +1 -0
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/schema.d.ts +6 -1
  12. package/dist/schema.js.map +1 -1
  13. package/dist/schema.mjs.map +1 -1
  14. package/dist/tests/thread-control-view.test.d.ts +1 -0
  15. package/dist/tests/thread-control-view.test.js +62 -0
  16. package/dist/tests/thread-control-view.test.js.map +1 -0
  17. package/dist/tests/thread-control-view.test.mjs +62 -0
  18. package/dist/tests/thread-control-view.test.mjs.map +1 -0
  19. package/dist/tests/view-file-preview.test.d.ts +1 -0
  20. package/dist/tests/view-file-preview.test.js +86 -0
  21. package/dist/tests/view-file-preview.test.js.map +1 -0
  22. package/dist/tests/view-file-preview.test.mjs +86 -0
  23. package/dist/tests/view-file-preview.test.mjs.map +1 -0
  24. package/dist/thread-activity.d.ts +2 -0
  25. package/dist/thread-activity.js +3 -0
  26. package/dist/thread-activity.js.map +1 -1
  27. package/dist/thread-activity.mjs +3 -0
  28. package/dist/thread-activity.mjs.map +1 -1
  29. package/dist/thread-control-view.d.ts +23 -0
  30. package/dist/thread-control-view.js +57 -0
  31. package/dist/thread-control-view.js.map +1 -0
  32. package/dist/thread-control-view.mjs +57 -0
  33. package/dist/thread-control-view.mjs.map +1 -0
  34. package/dist/types.stream.d.ts +10 -1
  35. package/dist/view-file-preview.d.ts +11 -0
  36. package/dist/view-file-preview.js +16 -0
  37. package/dist/view-file-preview.js.map +1 -0
  38. package/dist/view-file-preview.mjs +16 -0
  39. package/dist/view-file-preview.mjs.map +1 -0
  40. package/package.json +3 -3
@@ -0,0 +1,86 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { Client } from "../client.js";
3
+ describe('authenticated image previews', () => {
4
+ it.each([false, true])('reads one binary response with the full request context (group=%s)', async (group) => {
5
+ const bytes = new Uint8Array([82, 73, 70, 70, 0, 255]);
6
+ const fetch = vi.fn().mockImplementation(async () => new Response(bytes, {
7
+ headers: {
8
+ 'content-type': 'image/webp',
9
+ 'content-disposition': "inline; filename*=UTF-8''%E5%9B%BE.webp",
10
+ },
11
+ }));
12
+ const onRequest = vi.fn((_url, init) => ({
13
+ ...init,
14
+ headers: { ...init.headers, 'x-context': 'current' },
15
+ }));
16
+ const client = new Client({
17
+ apiUrl: 'https://example.test/proxy/api/ai/',
18
+ apiKey: 'test',
19
+ defaultHeaders: { 'organization-id': 'org' },
20
+ callerOptions: { fetch, maxRetries: 0 },
21
+ onRequest,
22
+ });
23
+ const view = group ? client.forGroupWorkbench('group/1').viewHosts : client.viewHosts;
24
+ const signal = new AbortController().signal;
25
+ const runtimeScope = { projectId: 'project', conversationId: 'conversation' };
26
+ const result = await view.readFilePreview('agent', 'assistant', 'view', { fileKey: 'file', targetId: 'version', variant: 'thumbnail' }, { runtimeScope, signal });
27
+ expect(result.fileName).toBe('图.webp');
28
+ expect(new Uint8Array(await result.blob.arrayBuffer())).toEqual(bytes);
29
+ expect(result.blob.type).toBe('image/webp');
30
+ expect(fetch).toHaveBeenCalledTimes(1);
31
+ const [url, init] = fetch.mock.calls[0];
32
+ expect(String(url)).toBe(`https://example.test/proxy/api/ai/${group ? 'groups/group%2F1/workbench/' : ''}workspace-files/view-sessions/preview`);
33
+ expect(init).toMatchObject({
34
+ method: 'POST',
35
+ signal,
36
+ credentials: 'omit',
37
+ redirect: 'error',
38
+ headers: {
39
+ 'x-context': 'current',
40
+ 'organization-id': 'org',
41
+ 'x-xpert-view-project-id': 'project',
42
+ },
43
+ });
44
+ expect(JSON.parse(String(init?.body))).toEqual({
45
+ hostType: 'agent',
46
+ hostId: 'assistant',
47
+ viewKey: 'view',
48
+ fileKey: 'file',
49
+ targetId: 'version',
50
+ variant: 'thumbnail',
51
+ runtimeScope,
52
+ });
53
+ expect(onRequest).toHaveBeenCalledOnce();
54
+ });
55
+ it.each([401, 403, 404, 415])('preserves HTTP %s without returning an error body as an image', async (status) => {
56
+ const fetch = vi
57
+ .fn()
58
+ .mockImplementation(async () => new Response('Denied', { status }));
59
+ const client = new Client({
60
+ apiUrl: 'https://example.test/api/ai',
61
+ callerOptions: { fetch, maxRetries: 0 },
62
+ });
63
+ await expect(client.viewHosts.readFilePreview('agent', 'assistant', 'view', {
64
+ fileKey: 'file',
65
+ variant: 'original',
66
+ })).rejects.toMatchObject({ status });
67
+ expect(fetch).toHaveBeenCalledOnce();
68
+ });
69
+ it.each([
70
+ ['text/html', 'inline; filename="image.png"'],
71
+ ['image/png', ''],
72
+ ['image/png', "inline; filename*=UTF-8''..%2Fimage.png"],
73
+ ])('rejects invalid binary metadata: %s %s', async (type, disposition) => {
74
+ const client = new Client({
75
+ apiUrl: 'https://example.test/api/ai',
76
+ callerOptions: {
77
+ fetch: async () => new Response('data', {
78
+ headers: { 'content-type': type, 'content-disposition': disposition },
79
+ }),
80
+ maxRetries: 0,
81
+ },
82
+ });
83
+ await expect(client.viewHosts.readFilePreview('agent', 'assistant', 'view', { fileKey: 'file' })).rejects.toThrow();
84
+ });
85
+ });
86
+ //# sourceMappingURL=view-file-preview.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-file-preview.test.js","sourceRoot":"","sources":["../../src/tests/view-file-preview.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { Client } from "../client.mjs";
3
+ describe('authenticated image previews', () => {
4
+ it.each([false, true])('reads one binary response with the full request context (group=%s)', async (group) => {
5
+ const bytes = new Uint8Array([82, 73, 70, 70, 0, 255]);
6
+ const fetch = vi.fn().mockImplementation(async () => new Response(bytes, {
7
+ headers: {
8
+ 'content-type': 'image/webp',
9
+ 'content-disposition': "inline; filename*=UTF-8''%E5%9B%BE.webp",
10
+ },
11
+ }));
12
+ const onRequest = vi.fn((_url, init) => ({
13
+ ...init,
14
+ headers: { ...init.headers, 'x-context': 'current' },
15
+ }));
16
+ const client = new Client({
17
+ apiUrl: 'https://example.test/proxy/api/ai/',
18
+ apiKey: 'test',
19
+ defaultHeaders: { 'organization-id': 'org' },
20
+ callerOptions: { fetch, maxRetries: 0 },
21
+ onRequest,
22
+ });
23
+ const view = group ? client.forGroupWorkbench('group/1').viewHosts : client.viewHosts;
24
+ const signal = new AbortController().signal;
25
+ const runtimeScope = { projectId: 'project', conversationId: 'conversation' };
26
+ const result = await view.readFilePreview('agent', 'assistant', 'view', { fileKey: 'file', targetId: 'version', variant: 'thumbnail' }, { runtimeScope, signal });
27
+ expect(result.fileName).toBe('图.webp');
28
+ expect(new Uint8Array(await result.blob.arrayBuffer())).toEqual(bytes);
29
+ expect(result.blob.type).toBe('image/webp');
30
+ expect(fetch).toHaveBeenCalledTimes(1);
31
+ const [url, init] = fetch.mock.calls[0];
32
+ expect(String(url)).toBe(`https://example.test/proxy/api/ai/${group ? 'groups/group%2F1/workbench/' : ''}workspace-files/view-sessions/preview`);
33
+ expect(init).toMatchObject({
34
+ method: 'POST',
35
+ signal,
36
+ credentials: 'omit',
37
+ redirect: 'error',
38
+ headers: {
39
+ 'x-context': 'current',
40
+ 'organization-id': 'org',
41
+ 'x-xpert-view-project-id': 'project',
42
+ },
43
+ });
44
+ expect(JSON.parse(String(init?.body))).toEqual({
45
+ hostType: 'agent',
46
+ hostId: 'assistant',
47
+ viewKey: 'view',
48
+ fileKey: 'file',
49
+ targetId: 'version',
50
+ variant: 'thumbnail',
51
+ runtimeScope,
52
+ });
53
+ expect(onRequest).toHaveBeenCalledOnce();
54
+ });
55
+ it.each([401, 403, 404, 415])('preserves HTTP %s without returning an error body as an image', async (status) => {
56
+ const fetch = vi
57
+ .fn()
58
+ .mockImplementation(async () => new Response('Denied', { status }));
59
+ const client = new Client({
60
+ apiUrl: 'https://example.test/api/ai',
61
+ callerOptions: { fetch, maxRetries: 0 },
62
+ });
63
+ await expect(client.viewHosts.readFilePreview('agent', 'assistant', 'view', {
64
+ fileKey: 'file',
65
+ variant: 'original',
66
+ })).rejects.toMatchObject({ status });
67
+ expect(fetch).toHaveBeenCalledOnce();
68
+ });
69
+ it.each([
70
+ ['text/html', 'inline; filename="image.png"'],
71
+ ['image/png', ''],
72
+ ['image/png', "inline; filename*=UTF-8''..%2Fimage.png"],
73
+ ])('rejects invalid binary metadata: %s %s', async (type, disposition) => {
74
+ const client = new Client({
75
+ apiUrl: 'https://example.test/api/ai',
76
+ callerOptions: {
77
+ fetch: async () => new Response('data', {
78
+ headers: { 'content-type': type, 'content-disposition': disposition },
79
+ }),
80
+ maxRetries: 0,
81
+ },
82
+ });
83
+ await expect(client.viewHosts.readFilePreview('agent', 'assistant', 'view', { fileKey: 'file' })).rejects.toThrow();
84
+ });
85
+ });
86
+ //# sourceMappingURL=view-file-preview.test.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-file-preview.test.mjs","sourceRoot":"","sources":["../../src/tests/view-file-preview.test.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,10 @@
1
+ import { type ThreadControlView } from './thread-control-view.js';
1
2
  import type { TMessageContentResourceCard } from './resource-card.js';
2
3
  /** Complete discovery snapshot. Reconnect replaces discovery state; tokens use runs.joinStream. */
3
4
  export interface ThreadActivitySnapshot {
4
5
  version: 1;
5
6
  threadId: string;
7
+ controlView?: ThreadControlView;
6
8
  runs: Array<{
7
9
  id: string;
8
10
  status: string;
@@ -1,9 +1,12 @@
1
+ import { isThreadControlView } from "./thread-control-view.js";
1
2
  export function isThreadActivitySnapshot(value) {
2
3
  if (!value || typeof value !== 'object' || !('version' in value) || value.version !== 1 ||
3
4
  !('threadId' in value) || typeof value.threadId !== 'string' ||
4
5
  !('runs' in value) || !Array.isArray(value.runs) ||
5
6
  !('cards' in value) || !Array.isArray(value.cards))
6
7
  return false;
8
+ if ('controlView' in value && value.controlView !== undefined && !isThreadControlView(value.controlView))
9
+ return false;
7
10
  // Card payloads use the shared resource-card parser at the rendering boundary.
8
11
  return value.runs.every((run) => !!run && typeof run === 'object' &&
9
12
  'id' in run && typeof run.id === 'string' &&
@@ -1 +1 @@
1
- {"version":3,"file":"thread-activity.js","sourceRoot":"","sources":["../src/thread-activity.ts"],"names":[],"mappings":"AAUA,MAAM,UAAU,wBAAwB,CAAC,KAAc;IACrD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC;QACnF,CAAC,CAAC,UAAU,IAAI,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ;QAC5D,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;QAChD,CAAC,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACrE,+EAA+E;IAC/E,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QACxE,IAAI,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ;QACzC,QAAQ,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ;QACjD,WAAW,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ;QACvD,WAAW,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ;QACvD,iBAAiB,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,eAAe,KAAK,QAAQ,CAAC,CAAC;AACzE,CAAC"}
1
+ {"version":3,"file":"thread-activity.js","sourceRoot":"","sources":["../src/thread-activity.ts"],"names":[],"mappings":"OAAO,EAAE,mBAAmB,EAA0B;AAYtD,MAAM,UAAU,wBAAwB,CAAC,KAAc;IACrD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC;QACnF,CAAC,CAAC,UAAU,IAAI,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ;QAC5D,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;QAChD,CAAC,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACrE,IAAI,aAAa,IAAI,KAAK,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,WAAW,CAAC;QAAE,OAAO,KAAK,CAAC;IACvH,+EAA+E;IAC/E,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QACxE,IAAI,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ;QACzC,QAAQ,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ;QACjD,WAAW,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ;QACvD,WAAW,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ;QACvD,iBAAiB,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,eAAe,KAAK,QAAQ,CAAC,CAAC;AACzE,CAAC"}
@@ -1,9 +1,12 @@
1
+ import { isThreadControlView } from "./thread-control-view.mjs";
1
2
  export function isThreadActivitySnapshot(value) {
2
3
  if (!value || typeof value !== 'object' || !('version' in value) || value.version !== 1 ||
3
4
  !('threadId' in value) || typeof value.threadId !== 'string' ||
4
5
  !('runs' in value) || !Array.isArray(value.runs) ||
5
6
  !('cards' in value) || !Array.isArray(value.cards))
6
7
  return false;
8
+ if ('controlView' in value && value.controlView !== undefined && !isThreadControlView(value.controlView))
9
+ return false;
7
10
  // Card payloads use the shared resource-card parser at the rendering boundary.
8
11
  return value.runs.every((run) => !!run && typeof run === 'object' &&
9
12
  'id' in run && typeof run.id === 'string' &&
@@ -1 +1 @@
1
- {"version":3,"file":"thread-activity.mjs","sourceRoot":"","sources":["../src/thread-activity.ts"],"names":[],"mappings":"AAUA,MAAM,UAAU,wBAAwB,CAAC,KAAc;IACrD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC;QACnF,CAAC,CAAC,UAAU,IAAI,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ;QAC5D,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;QAChD,CAAC,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACrE,+EAA+E;IAC/E,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QACxE,IAAI,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ;QACzC,QAAQ,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ;QACjD,WAAW,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ;QACvD,WAAW,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ;QACvD,iBAAiB,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,eAAe,KAAK,QAAQ,CAAC,CAAC;AACzE,CAAC"}
1
+ {"version":3,"file":"thread-activity.mjs","sourceRoot":"","sources":["../src/thread-activity.ts"],"names":[],"mappings":"OAAO,EAAE,mBAAmB,EAA0B;AAYtD,MAAM,UAAU,wBAAwB,CAAC,KAAc;IACrD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC;QACnF,CAAC,CAAC,UAAU,IAAI,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ;QAC5D,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;QAChD,CAAC,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACrE,IAAI,aAAa,IAAI,KAAK,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,WAAW,CAAC;QAAE,OAAO,KAAK,CAAC;IACvH,+EAA+E;IAC/E,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QACxE,IAAI,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ;QACzC,QAAQ,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ;QACjD,WAAW,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ;QACvD,WAAW,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ;QACvD,iBAAiB,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,eAAe,KAAK,QAAQ,CAAC,CAAC;AACzE,CAAC"}
@@ -0,0 +1,23 @@
1
+ /** Public hints only. The host revalidates every action against its durable execution state. */
2
+ export type ThreadControlActions = {
3
+ canPause: boolean;
4
+ canResume: boolean;
5
+ canTerminate: boolean;
6
+ canSend: boolean;
7
+ canQueue: boolean;
8
+ canRetry: boolean;
9
+ canRespond: boolean;
10
+ };
11
+ export type ThreadControlView = {
12
+ version: 1;
13
+ threadId: string;
14
+ runId: string | null;
15
+ /** Decimal durable clock; do not compare revisions as strings or timestamps. */
16
+ revision: string;
17
+ state: 'idle' | 'running' | 'pausing' | 'paused' | 'stopping' | 'settling' | 'input_required' | 'confirming' | 'completed' | 'stopped' | 'failed';
18
+ actions: ThreadControlActions;
19
+ pauseId?: string;
20
+ };
21
+ export declare function isThreadControlView(value: unknown): value is ThreadControlView;
22
+ /** Revisions order the whole thread, including replacement runs. Never let an old response restore controls. */
23
+ export declare function mergeThreadControlView(previous: ThreadControlView | undefined, incoming: ThreadControlView): ThreadControlView;
@@ -0,0 +1,57 @@
1
+ const states = [
2
+ 'idle',
3
+ 'running',
4
+ 'pausing',
5
+ 'paused',
6
+ 'stopping',
7
+ 'settling',
8
+ 'input_required',
9
+ 'confirming',
10
+ 'completed',
11
+ 'stopped',
12
+ 'failed',
13
+ ];
14
+ export function isThreadControlView(value) {
15
+ if (!value ||
16
+ typeof value !== 'object' ||
17
+ !('version' in value) ||
18
+ value.version !== 1 ||
19
+ !('threadId' in value) ||
20
+ typeof value.threadId !== 'string' ||
21
+ !value.threadId ||
22
+ !('runId' in value) ||
23
+ !(value.runId === null || (typeof value.runId === 'string' && value.runId.length > 0)) ||
24
+ !('revision' in value) ||
25
+ typeof value.revision !== 'string' ||
26
+ !/^[1-9][0-9]*$/.test(value.revision) ||
27
+ !('state' in value) ||
28
+ typeof value.state !== 'string' ||
29
+ !states.includes(value.state) ||
30
+ ('pauseId' in value && typeof value.pauseId !== 'string') ||
31
+ !('actions' in value) ||
32
+ !value.actions ||
33
+ typeof value.actions !== 'object')
34
+ return false;
35
+ const actions = value.actions;
36
+ return ('canPause' in actions &&
37
+ typeof actions.canPause === 'boolean' &&
38
+ 'canResume' in actions &&
39
+ typeof actions.canResume === 'boolean' &&
40
+ 'canTerminate' in actions &&
41
+ typeof actions.canTerminate === 'boolean' &&
42
+ 'canSend' in actions &&
43
+ typeof actions.canSend === 'boolean' &&
44
+ 'canQueue' in actions &&
45
+ typeof actions.canQueue === 'boolean' &&
46
+ 'canRetry' in actions &&
47
+ typeof actions.canRetry === 'boolean' &&
48
+ 'canRespond' in actions &&
49
+ typeof actions.canRespond === 'boolean');
50
+ }
51
+ /** Revisions order the whole thread, including replacement runs. Never let an old response restore controls. */
52
+ export function mergeThreadControlView(previous, incoming) {
53
+ if (!previous || previous.threadId !== incoming.threadId)
54
+ return incoming;
55
+ return BigInt(incoming.revision) < BigInt(previous.revision) ? previous : incoming;
56
+ }
57
+ //# sourceMappingURL=thread-control-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread-control-view.js","sourceRoot":"","sources":["../src/thread-control-view.ts"],"names":[],"mappings":"AA+BA,MAAM,MAAM,GAAsB;IAChC,MAAM;IACN,SAAS;IACT,SAAS;IACT,QAAQ;IACR,UAAU;IACV,UAAU;IACV,gBAAgB;IAChB,YAAY;IACZ,WAAW;IACX,SAAS;IACT,QAAQ;CACT,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,IACE,CAAC,KAAK;QACN,OAAO,KAAK,KAAK,QAAQ;QACzB,CAAC,CAAC,SAAS,IAAI,KAAK,CAAC;QACrB,KAAK,CAAC,OAAO,KAAK,CAAC;QACnB,CAAC,CAAC,UAAU,IAAI,KAAK,CAAC;QACtB,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ;QAClC,CAAC,KAAK,CAAC,QAAQ;QACf,CAAC,CAAC,OAAO,IAAI,KAAK,CAAC;QACnB,CAAC,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACtF,CAAC,CAAC,UAAU,IAAI,KAAK,CAAC;QACtB,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ;QAClC,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;QACrC,CAAC,CAAC,OAAO,IAAI,KAAK,CAAC;QACnB,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ;QAC/B,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC;QAC7B,CAAC,SAAS,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,CAAC;QACzD,CAAC,CAAC,SAAS,IAAI,KAAK,CAAC;QACrB,CAAC,KAAK,CAAC,OAAO;QACd,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;QAEjC,OAAO,KAAK,CAAC;IACf,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC9B,OAAO,CACL,UAAU,IAAI,OAAO;QACrB,OAAO,OAAO,CAAC,QAAQ,KAAK,SAAS;QACrC,WAAW,IAAI,OAAO;QACtB,OAAO,OAAO,CAAC,SAAS,KAAK,SAAS;QACtC,cAAc,IAAI,OAAO;QACzB,OAAO,OAAO,CAAC,YAAY,KAAK,SAAS;QACzC,SAAS,IAAI,OAAO;QACpB,OAAO,OAAO,CAAC,OAAO,KAAK,SAAS;QACpC,UAAU,IAAI,OAAO;QACrB,OAAO,OAAO,CAAC,QAAQ,KAAK,SAAS;QACrC,UAAU,IAAI,OAAO;QACrB,OAAO,OAAO,CAAC,QAAQ,KAAK,SAAS;QACrC,YAAY,IAAI,OAAO;QACvB,OAAO,OAAO,CAAC,UAAU,KAAK,SAAS,CACxC,CAAC;AACJ,CAAC;AAED,gHAAgH;AAChH,MAAM,UAAU,sBAAsB,CACpC,QAAuC,EACvC,QAA2B;IAE3B,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,KAAK,QAAQ,CAAC,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC1E,OAAO,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;AACrF,CAAC"}
@@ -0,0 +1,57 @@
1
+ const states = [
2
+ 'idle',
3
+ 'running',
4
+ 'pausing',
5
+ 'paused',
6
+ 'stopping',
7
+ 'settling',
8
+ 'input_required',
9
+ 'confirming',
10
+ 'completed',
11
+ 'stopped',
12
+ 'failed',
13
+ ];
14
+ export function isThreadControlView(value) {
15
+ if (!value ||
16
+ typeof value !== 'object' ||
17
+ !('version' in value) ||
18
+ value.version !== 1 ||
19
+ !('threadId' in value) ||
20
+ typeof value.threadId !== 'string' ||
21
+ !value.threadId ||
22
+ !('runId' in value) ||
23
+ !(value.runId === null || (typeof value.runId === 'string' && value.runId.length > 0)) ||
24
+ !('revision' in value) ||
25
+ typeof value.revision !== 'string' ||
26
+ !/^[1-9][0-9]*$/.test(value.revision) ||
27
+ !('state' in value) ||
28
+ typeof value.state !== 'string' ||
29
+ !states.includes(value.state) ||
30
+ ('pauseId' in value && typeof value.pauseId !== 'string') ||
31
+ !('actions' in value) ||
32
+ !value.actions ||
33
+ typeof value.actions !== 'object')
34
+ return false;
35
+ const actions = value.actions;
36
+ return ('canPause' in actions &&
37
+ typeof actions.canPause === 'boolean' &&
38
+ 'canResume' in actions &&
39
+ typeof actions.canResume === 'boolean' &&
40
+ 'canTerminate' in actions &&
41
+ typeof actions.canTerminate === 'boolean' &&
42
+ 'canSend' in actions &&
43
+ typeof actions.canSend === 'boolean' &&
44
+ 'canQueue' in actions &&
45
+ typeof actions.canQueue === 'boolean' &&
46
+ 'canRetry' in actions &&
47
+ typeof actions.canRetry === 'boolean' &&
48
+ 'canRespond' in actions &&
49
+ typeof actions.canRespond === 'boolean');
50
+ }
51
+ /** Revisions order the whole thread, including replacement runs. Never let an old response restore controls. */
52
+ export function mergeThreadControlView(previous, incoming) {
53
+ if (!previous || previous.threadId !== incoming.threadId)
54
+ return incoming;
55
+ return BigInt(incoming.revision) < BigInt(previous.revision) ? previous : incoming;
56
+ }
57
+ //# sourceMappingURL=thread-control-view.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread-control-view.mjs","sourceRoot":"","sources":["../src/thread-control-view.ts"],"names":[],"mappings":"AA+BA,MAAM,MAAM,GAAsB;IAChC,MAAM;IACN,SAAS;IACT,SAAS;IACT,QAAQ;IACR,UAAU;IACV,UAAU;IACV,gBAAgB;IAChB,YAAY;IACZ,WAAW;IACX,SAAS;IACT,QAAQ;CACT,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,IACE,CAAC,KAAK;QACN,OAAO,KAAK,KAAK,QAAQ;QACzB,CAAC,CAAC,SAAS,IAAI,KAAK,CAAC;QACrB,KAAK,CAAC,OAAO,KAAK,CAAC;QACnB,CAAC,CAAC,UAAU,IAAI,KAAK,CAAC;QACtB,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ;QAClC,CAAC,KAAK,CAAC,QAAQ;QACf,CAAC,CAAC,OAAO,IAAI,KAAK,CAAC;QACnB,CAAC,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACtF,CAAC,CAAC,UAAU,IAAI,KAAK,CAAC;QACtB,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ;QAClC,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;QACrC,CAAC,CAAC,OAAO,IAAI,KAAK,CAAC;QACnB,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ;QAC/B,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC;QAC7B,CAAC,SAAS,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,CAAC;QACzD,CAAC,CAAC,SAAS,IAAI,KAAK,CAAC;QACrB,CAAC,KAAK,CAAC,OAAO;QACd,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;QAEjC,OAAO,KAAK,CAAC;IACf,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC9B,OAAO,CACL,UAAU,IAAI,OAAO;QACrB,OAAO,OAAO,CAAC,QAAQ,KAAK,SAAS;QACrC,WAAW,IAAI,OAAO;QACtB,OAAO,OAAO,CAAC,SAAS,KAAK,SAAS;QACtC,cAAc,IAAI,OAAO;QACzB,OAAO,OAAO,CAAC,YAAY,KAAK,SAAS;QACzC,SAAS,IAAI,OAAO;QACpB,OAAO,OAAO,CAAC,OAAO,KAAK,SAAS;QACpC,UAAU,IAAI,OAAO;QACrB,OAAO,OAAO,CAAC,QAAQ,KAAK,SAAS;QACrC,UAAU,IAAI,OAAO;QACrB,OAAO,OAAO,CAAC,QAAQ,KAAK,SAAS;QACrC,YAAY,IAAI,OAAO;QACvB,OAAO,OAAO,CAAC,UAAU,KAAK,SAAS,CACxC,CAAC;AACJ,CAAC;AAED,gHAAgH;AAChH,MAAM,UAAU,sBAAsB,CACpC,QAAuC,EACvC,QAA2B;IAE3B,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,KAAK,QAAQ,CAAC,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC1E,OAAO,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;AACrF,CAAC"}
@@ -1,7 +1,6 @@
1
1
  import type { Message } from "./types.messages.js";
2
2
  import type { Interrupt, Metadata, Config, ThreadTask } from "./schema.js";
3
3
  /**
4
- import type { SubgraphCheckpointsStreamEvent } from "./types.stream.subgraph.js";
5
4
  * Stream modes
6
5
  * - "values": Stream only the state values.
7
6
  * - "messages": Stream complete messages.
@@ -34,13 +33,16 @@ export type ValuesStreamEvent<StateType> = {
34
33
  event: "values";
35
34
  data: StateType;
36
35
  };
36
+ export type SubgraphValuesStreamEvent<StateType> = AsSubgraph<ValuesStreamEvent<StateType>>;
37
37
  /**
38
38
  * Stream event with message chunks coming from LLM invocations inside nodes.
39
39
  */
40
40
  export type MessagesTupleStreamEvent = {
41
+ id?: string;
41
42
  event: "messages";
42
43
  data: [message: Message, config: MessageTupleMetadata];
43
44
  };
45
+ export type SubgraphMessagesTupleStreamEvent = AsSubgraph<MessagesTupleStreamEvent>;
44
46
  /**
45
47
  * Metadata stream event with information about the run and thread
46
48
  */
@@ -63,6 +65,7 @@ export type ErrorStreamEvent = {
63
65
  message: string;
64
66
  };
65
67
  };
68
+ export type SubgraphErrorStreamEvent = AsSubgraph<ErrorStreamEvent>;
66
69
  /**
67
70
  * Stream event with updates to the state after each step.
68
71
  * The streamed outputs include the name of the node that
@@ -75,13 +78,16 @@ export type UpdatesStreamEvent<UpdateType> = {
75
78
  [node: string]: UpdateType;
76
79
  };
77
80
  };
81
+ export type SubgraphUpdatesStreamEvent<UpdateType> = AsSubgraph<UpdatesStreamEvent<UpdateType>>;
78
82
  /**
79
83
  * Streaming custom data from inside the nodes.
80
84
  */
81
85
  export type CustomStreamEvent<T> = {
86
+ id?: string;
82
87
  event: "custom";
83
88
  data: T;
84
89
  };
90
+ export type SubgraphCustomStreamEvent<T> = AsSubgraph<CustomStreamEvent<T>>;
85
91
  type MessagesMetadataStreamEvent = {
86
92
  id?: string;
87
93
  event: "messages/metadata";
@@ -151,6 +157,7 @@ type SubgraphCheckpointsStreamEvent<StateType> = AsSubgraph<CheckpointsStreamEve
151
157
  * @deprecated Use `streamMode: "messages-tuple"` instead.
152
158
  */
153
159
  export type MessagesStreamEvent = MessagesMetadataStreamEvent | MessagesCompleteStreamEvent | MessagesPartialStreamEvent;
160
+ export type SubgraphMessagesStreamEvent = AsSubgraph<MessagesMetadataStreamEvent> | AsSubgraph<MessagesCompleteStreamEvent> | AsSubgraph<MessagesPartialStreamEvent>;
154
161
  /**
155
162
  * Stream event with detailed debug information.
156
163
  */
@@ -159,6 +166,7 @@ export type DebugStreamEvent = {
159
166
  event: "debug";
160
167
  data: unknown;
161
168
  };
169
+ export type SubgraphDebugStreamEvent = AsSubgraph<DebugStreamEvent>;
162
170
  /**
163
171
  * Stream event with events occurring during execution.
164
172
  */
@@ -175,6 +183,7 @@ export type EventsStreamEvent = {
175
183
  data: unknown;
176
184
  };
177
185
  };
186
+ export type SubgraphEventsStreamEvent = AsSubgraph<EventsStreamEvent>;
178
187
  /**
179
188
  * Stream event with a feedback key to signed URL map. Set `feedbackKeys` in
180
189
  * the `RunsStreamPayload` to receive this event.
@@ -0,0 +1,11 @@
1
+ /** A preview is resolved and authorized in the same request that returns its bytes. */
2
+ export interface XpertViewFilePreviewRequest {
3
+ fileKey: string;
4
+ targetId?: string;
5
+ variant?: 'thumbnail' | 'original';
6
+ }
7
+ export interface XpertViewFilePreviewResult {
8
+ blob: Blob;
9
+ fileName: string;
10
+ }
11
+ export declare function readViewFilePreviewResponse(response: Response): Promise<XpertViewFilePreviewResult>;
@@ -0,0 +1,16 @@
1
+ export async function readViewFilePreviewResponse(response) {
2
+ const disposition = response.headers.get('content-disposition') ?? '';
3
+ const encoded = /filename\*=UTF-8''([^;]+)/i.exec(disposition)?.[1];
4
+ const fileName = encoded
5
+ ? decodeURIComponent(encoded)
6
+ : /filename="([^"\r\n]+)"/i.exec(disposition)?.[1];
7
+ if (!fileName || /[/\\\u0000-\u001f\u007f]/.test(fileName)) {
8
+ throw new Error('Invalid image preview filename.');
9
+ }
10
+ const blob = await response.blob();
11
+ if (!['image/png', 'image/jpeg', 'image/webp', 'image/gif'].includes(blob.type.split(';')[0].trim())) {
12
+ throw new Error('Invalid image preview content type.');
13
+ }
14
+ return { blob, fileName };
15
+ }
16
+ //# sourceMappingURL=view-file-preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-file-preview.js","sourceRoot":"","sources":["../src/view-file-preview.ts"],"names":[],"mappings":"AAYA,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,QAAkB;IAElB,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,IAAI,EAAE,CAAC;IACtE,MAAM,OAAO,GAAG,4BAA4B,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,QAAQ,GAAG,OAAO;QACtB,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC;QAC7B,CAAC,CAAC,yBAAyB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACrD,IAAI,CAAC,QAAQ,IAAI,0BAA0B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACnC,IACE,CAAC,CAAC,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAChG,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,16 @@
1
+ export async function readViewFilePreviewResponse(response) {
2
+ const disposition = response.headers.get('content-disposition') ?? '';
3
+ const encoded = /filename\*=UTF-8''([^;]+)/i.exec(disposition)?.[1];
4
+ const fileName = encoded
5
+ ? decodeURIComponent(encoded)
6
+ : /filename="([^"\r\n]+)"/i.exec(disposition)?.[1];
7
+ if (!fileName || /[/\\\u0000-\u001f\u007f]/.test(fileName)) {
8
+ throw new Error('Invalid image preview filename.');
9
+ }
10
+ const blob = await response.blob();
11
+ if (!['image/png', 'image/jpeg', 'image/webp', 'image/gif'].includes(blob.type.split(';')[0].trim())) {
12
+ throw new Error('Invalid image preview content type.');
13
+ }
14
+ return { blob, fileName };
15
+ }
16
+ //# sourceMappingURL=view-file-preview.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-file-preview.mjs","sourceRoot":"","sources":["../src/view-file-preview.ts"],"names":[],"mappings":"AAYA,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,QAAkB;IAElB,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,IAAI,EAAE,CAAC;IACtE,MAAM,OAAO,GAAG,4BAA4B,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,QAAQ,GAAG,OAAO;QACtB,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC;QAC7B,CAAC,CAAC,yBAAyB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACrD,IAAI,CAAC,QAAQ,IAAI,0BAA0B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACnC,IACE,CAAC,CAAC,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAChG,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC5B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xpert-ai/xpert-sdk",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "SDK Core functionality package",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -20,11 +20,11 @@
20
20
  "p-queue": "^6.6.2",
21
21
  "p-retry": "4",
22
22
  "socket.io-client": "^4.8.4",
23
- "uuid": "^9.0.0"
23
+ "uuid": "^9.0.0",
24
+ "@types/json-schema": "^7.0.15"
24
25
  },
25
26
  "devDependencies": {
26
27
  "@types/node": "^22.16.3",
27
- "@types/json-schema": "^7.0.15",
28
28
  "rimraf": "^6.0.1",
29
29
  "typescript": "^5.8.3",
30
30
  "vitest": "^3.2.4",