@deepseek-ai/dsh-api-remotes 0.1.1-rc.2 → 0.1.2-alpha.2
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/README.i18n.yaml +2 -2
- package/README.md +53 -10
- package/README.zh.md +53 -11
- package/lib/client.js +3723 -755
- package/lib/index.js +180 -150
- package/lib/types/client/index.d.ts +22 -6
- package/lib/types/client/index.js +8 -2
- package/lib/types/index.d.ts +6 -4
- package/lib/types/index.js +119 -12
- package/lib/types/remote-events.d.ts +57 -7
- package/lib/types/remote-events.js +20 -17
- package/lib/types/types.d.ts +1 -1
- package/package.json +30 -39
- package/lib/types/agent-lookup.d.ts +0 -95
- package/lib/types/agent-lookup.js +0 -166
|
@@ -6,12 +6,62 @@
|
|
|
6
6
|
* type-only.
|
|
7
7
|
*/
|
|
8
8
|
/**
|
|
9
|
-
* Host events this application forwards
|
|
10
|
-
*
|
|
11
|
-
* the payload is its argument list, so this array is simultaneously the whole
|
|
12
|
-
* control point over what a consumer can receive and the legal key set of
|
|
13
|
-
* `ctx.remote.$on`. Forwarding one more event is an entry here and nothing
|
|
14
|
-
* else.
|
|
9
|
+
* Host events this application forwards without renaming. The explicit mode is
|
|
10
|
+
* both the Host dispatch strategy and the legal key set of `ctx.remote.$on`.
|
|
15
11
|
*/
|
|
16
|
-
export declare const API_REMOTE_FORWARDED_EVENTS: readonly [
|
|
12
|
+
export declare const API_REMOTE_FORWARDED_EVENTS: readonly [{
|
|
13
|
+
readonly event: "agent-preset/selected";
|
|
14
|
+
readonly mode: "emit";
|
|
15
|
+
}, {
|
|
16
|
+
readonly event: "approval/request";
|
|
17
|
+
readonly mode: "waterfall";
|
|
18
|
+
}, {
|
|
19
|
+
readonly event: "api-session/activity";
|
|
20
|
+
readonly mode: "emit";
|
|
21
|
+
}, {
|
|
22
|
+
readonly event: "api-session/added";
|
|
23
|
+
readonly mode: "emit";
|
|
24
|
+
}, {
|
|
25
|
+
readonly event: "api-session/error";
|
|
26
|
+
readonly mode: "emit";
|
|
27
|
+
}, {
|
|
28
|
+
readonly event: "api-session/removed";
|
|
29
|
+
readonly mode: "emit";
|
|
30
|
+
}, {
|
|
31
|
+
readonly event: "api-session/status";
|
|
32
|
+
readonly mode: "emit";
|
|
33
|
+
}, {
|
|
34
|
+
readonly event: "commands/change";
|
|
35
|
+
readonly mode: "emit";
|
|
36
|
+
}, {
|
|
37
|
+
readonly event: "credentials/reference-updated";
|
|
38
|
+
readonly mode: "emit";
|
|
39
|
+
}, {
|
|
40
|
+
readonly event: "cordis/request-run";
|
|
41
|
+
readonly mode: "emit";
|
|
42
|
+
}, {
|
|
43
|
+
readonly event: "cordis/request-run-resolved";
|
|
44
|
+
readonly mode: "emit";
|
|
45
|
+
}, {
|
|
46
|
+
readonly event: "cordis/dynamic-package";
|
|
47
|
+
readonly mode: "emit";
|
|
48
|
+
}, {
|
|
49
|
+
readonly event: "cordis/dynamic-retract";
|
|
50
|
+
readonly mode: "emit";
|
|
51
|
+
}, {
|
|
52
|
+
readonly event: "cordis/inspect-query";
|
|
53
|
+
readonly mode: "emit";
|
|
54
|
+
}, {
|
|
55
|
+
readonly event: "cordis/inspect-query-resolved";
|
|
56
|
+
readonly mode: "emit";
|
|
57
|
+
}, {
|
|
58
|
+
readonly event: "llm/adapters-updated";
|
|
59
|
+
readonly mode: "emit";
|
|
60
|
+
}, {
|
|
61
|
+
readonly event: "settings/document-updated";
|
|
62
|
+
readonly mode: "emit";
|
|
63
|
+
}, {
|
|
64
|
+
readonly event: "user-questions/request";
|
|
65
|
+
readonly mode: "waterfall";
|
|
66
|
+
}];
|
|
17
67
|
//# sourceMappingURL=remote-events.d.ts.map
|
|
@@ -6,24 +6,27 @@
|
|
|
6
6
|
* type-only.
|
|
7
7
|
*/
|
|
8
8
|
/**
|
|
9
|
-
* Host events this application forwards
|
|
10
|
-
*
|
|
11
|
-
* the payload is its argument list, so this array is simultaneously the whole
|
|
12
|
-
* control point over what a consumer can receive and the legal key set of
|
|
13
|
-
* `ctx.remote.$on`. Forwarding one more event is an entry here and nothing
|
|
14
|
-
* else.
|
|
9
|
+
* Host events this application forwards without renaming. The explicit mode is
|
|
10
|
+
* both the Host dispatch strategy and the legal key set of `ctx.remote.$on`.
|
|
15
11
|
*/
|
|
16
12
|
export const API_REMOTE_FORWARDED_EVENTS = [
|
|
17
|
-
'agent-preset/selected',
|
|
18
|
-
'
|
|
19
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'
|
|
26
|
-
'
|
|
27
|
-
'
|
|
13
|
+
{ event: 'agent-preset/selected', mode: 'emit' },
|
|
14
|
+
{ event: 'approval/request', mode: 'waterfall' },
|
|
15
|
+
{ event: 'api-session/activity', mode: 'emit' },
|
|
16
|
+
{ event: 'api-session/added', mode: 'emit' },
|
|
17
|
+
{ event: 'api-session/error', mode: 'emit' },
|
|
18
|
+
{ event: 'api-session/removed', mode: 'emit' },
|
|
19
|
+
{ event: 'api-session/status', mode: 'emit' },
|
|
20
|
+
{ event: 'commands/change', mode: 'emit' },
|
|
21
|
+
{ event: 'credentials/reference-updated', mode: 'emit' },
|
|
22
|
+
{ event: 'cordis/request-run', mode: 'emit' },
|
|
23
|
+
{ event: 'cordis/request-run-resolved', mode: 'emit' },
|
|
24
|
+
{ event: 'cordis/dynamic-package', mode: 'emit' },
|
|
25
|
+
{ event: 'cordis/dynamic-retract', mode: 'emit' },
|
|
26
|
+
{ event: 'cordis/inspect-query', mode: 'emit' },
|
|
27
|
+
{ event: 'cordis/inspect-query-resolved', mode: 'emit' },
|
|
28
|
+
{ event: 'llm/adapters-updated', mode: 'emit' },
|
|
29
|
+
{ event: 'settings/document-updated', mode: 'emit' },
|
|
30
|
+
{ event: 'user-questions/request', mode: 'waterfall' },
|
|
28
31
|
];
|
|
29
32
|
//# sourceMappingURL=remote-events.js.map
|
package/lib/types/types.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { API_REMOTE_FORWARDED_EVENTS } from './remote-events.ts';
|
|
12
12
|
/** Type projection of the allowlist; the consumer and the Host read this one. */
|
|
13
|
-
export type ApiRemoteForwardedEvent = typeof API_REMOTE_FORWARDED_EVENTS[number];
|
|
13
|
+
export type ApiRemoteForwardedEvent = typeof API_REMOTE_FORWARDED_EVENTS[number]['event'];
|
|
14
14
|
declare module '@deepseek-ai/dsh-typert-protocol' {
|
|
15
15
|
interface TypertRemoteEventSelection extends Record<ApiRemoteForwardedEvent, true> {
|
|
16
16
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-api-remotes",
|
|
3
|
-
"description": "Remote BFF assembly
|
|
4
|
-
"version": "0.1.
|
|
3
|
+
"description": "Remote BFF assembly for application-selected Host capabilities",
|
|
4
|
+
"version": "0.1.2-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -51,47 +51,38 @@
|
|
|
51
51
|
"lib/types/**/*.d.ts"
|
|
52
52
|
],
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@deepseek-ai/dsh-
|
|
54
|
+
"@deepseek-ai/dsh-deque": "^0.1.2-alpha.2",
|
|
55
|
+
"@deepseek-ai/dsh-session": "^0.1.2-alpha.2",
|
|
56
|
+
"@deepseek-ai/dsh-util-values": "^0.1.2-alpha.2"
|
|
55
57
|
},
|
|
56
58
|
"peerDependencies": {
|
|
57
|
-
"@deepseek-ai/
|
|
58
|
-
"@deepseek-ai/dsh-
|
|
59
|
-
"@deepseek-ai/dsh-agent-presets": "^0.1.1-rc.2",
|
|
60
|
-
"@deepseek-ai/dsh-commands": "^0.1.1-rc.2",
|
|
61
|
-
"@deepseek-ai/dsh-credentials": "^0.1.1-rc.2",
|
|
62
|
-
"@deepseek-ai/dsh-file-reference": "^0.1.1-rc.2",
|
|
63
|
-
"@deepseek-ai/dsh-goal": "^0.1.1-rc.2",
|
|
64
|
-
"@deepseek-ai/dsh-host-plugin-inventory": "^0.1.1-rc.2",
|
|
65
|
-
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
66
|
-
"@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
|
|
67
|
-
"@deepseek-ai/dsh-cordis-host-runner": "^0.1.1-rc.2",
|
|
68
|
-
"@deepseek-ai/dsh-message-feedback": "^0.1.1-rc.2",
|
|
69
|
-
"@deepseek-ai/dsh-session-persistence": "^0.1.1-rc.2",
|
|
70
|
-
"@deepseek-ai/dsh-session-reference": "^0.1.1-rc.2",
|
|
71
|
-
"@deepseek-ai/dsh-settings": "^0.1.1-rc.2",
|
|
72
|
-
"@deepseek-ai/dsh-typert-registry": "^0.1.1-rc.2",
|
|
73
|
-
"@deepseek-ai/cordis": "^4.0.1",
|
|
74
|
-
"@deepseek-ai/dsh-session": "^0.1.1-rc.2"
|
|
59
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
60
|
+
"@deepseek-ai/dsh-scope": "^0.1.2-alpha.2"
|
|
75
61
|
},
|
|
76
62
|
"devDependencies": {
|
|
77
|
-
"@deepseek-ai/
|
|
78
|
-
"@deepseek-ai/dsh-agent": "^0.1.
|
|
79
|
-
"@deepseek-ai/dsh-api-gateway": "^0.1.
|
|
80
|
-
"@deepseek-ai/dsh-
|
|
81
|
-
"@deepseek-ai/dsh-
|
|
82
|
-
"@deepseek-ai/dsh-
|
|
83
|
-
"@deepseek-ai/dsh-
|
|
84
|
-
"@deepseek-ai/dsh-
|
|
85
|
-
"@deepseek-ai/dsh-
|
|
86
|
-
"@deepseek-ai/dsh-
|
|
87
|
-
"@deepseek-ai/dsh-
|
|
88
|
-
"@deepseek-ai/dsh-
|
|
89
|
-
"@deepseek-ai/dsh-session": "^0.1.
|
|
90
|
-
"@deepseek-ai/dsh-
|
|
91
|
-
"@deepseek-ai/dsh-
|
|
92
|
-
"@deepseek-ai/dsh-
|
|
93
|
-
"@deepseek-ai/dsh-
|
|
94
|
-
"@deepseek-ai/
|
|
63
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
64
|
+
"@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.2",
|
|
65
|
+
"@deepseek-ai/dsh-api-gateway": "^0.1.2-alpha.2",
|
|
66
|
+
"@deepseek-ai/dsh-api-settings-controller": "^0.1.2-alpha.2",
|
|
67
|
+
"@deepseek-ai/dsh-api-workspace-controller": "^0.1.2-alpha.2",
|
|
68
|
+
"@deepseek-ai/dsh-credentials": "^0.1.2-alpha.2",
|
|
69
|
+
"@deepseek-ai/dsh-commands": "^0.1.2-alpha.2",
|
|
70
|
+
"@deepseek-ai/dsh-host-plugin-inventory": "^0.1.2-alpha.2",
|
|
71
|
+
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
|
|
72
|
+
"@deepseek-ai/dsh-goal": "^0.1.2-alpha.2",
|
|
73
|
+
"@deepseek-ai/dsh-message-feedback": "^0.1.2-alpha.2",
|
|
74
|
+
"@deepseek-ai/dsh-llm": "^0.1.2-alpha.2",
|
|
75
|
+
"@deepseek-ai/dsh-session-reference": "^0.1.2-alpha.2",
|
|
76
|
+
"@deepseek-ai/dsh-file-reference": "^0.1.2-alpha.2",
|
|
77
|
+
"@deepseek-ai/dsh-subagent": "^0.1.2-alpha.2",
|
|
78
|
+
"@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.2",
|
|
79
|
+
"@deepseek-ai/dsh-user-questions": "^0.1.2-alpha.2",
|
|
80
|
+
"@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.2",
|
|
81
|
+
"@deepseek-ai/dsh-settings": "^0.1.2-alpha.2",
|
|
82
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.2",
|
|
83
|
+
"@deepseek-ai/dsh-scope": "^0.1.2-alpha.2",
|
|
84
|
+
"@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.2",
|
|
85
|
+
"@deepseek-ai/dsh-cordis-host-runner": "^0.1.2-alpha.2"
|
|
95
86
|
},
|
|
96
87
|
"scripts": {
|
|
97
88
|
"bundle": "tsdown",
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
/** Host BFF policy for resolving Remote Agent and Session identities. */
|
|
2
|
-
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
-
import type { Agent, AgentOptions, AgentSetup } from '@deepseek-ai/dsh-agent';
|
|
4
|
-
import type { Session, SessionEvent, SessionHeader, SessionId } from '@deepseek-ai/dsh-session';
|
|
5
|
-
/** Caller-facing failures preserved by the Gateway's RPC adapter. */
|
|
6
|
-
export type ApiRemoteLookupError = {
|
|
7
|
-
readonly code: 'agent-busy';
|
|
8
|
-
readonly message: string;
|
|
9
|
-
readonly details: {
|
|
10
|
-
readonly reason: string;
|
|
11
|
-
};
|
|
12
|
-
} | {
|
|
13
|
-
readonly code: 'session-not-found';
|
|
14
|
-
readonly message: string;
|
|
15
|
-
readonly details: {
|
|
16
|
-
readonly sessionId: SessionId;
|
|
17
|
-
};
|
|
18
|
-
} | {
|
|
19
|
-
readonly code: 'internal';
|
|
20
|
-
readonly message: string;
|
|
21
|
-
readonly details: Record<never, never>;
|
|
22
|
-
};
|
|
23
|
-
/** Result of resolving one session identity to its live Agent. */
|
|
24
|
-
export type ApiRemoteAgentResult = {
|
|
25
|
-
readonly agent: Agent;
|
|
26
|
-
} | {
|
|
27
|
-
readonly error: ApiRemoteLookupError;
|
|
28
|
-
};
|
|
29
|
-
/** Resume configuration supplied by the owning Host composition. */
|
|
30
|
-
export interface ApiRemoteAgentOptions {
|
|
31
|
-
/** Read the per-Agent defaults when a cold identity must resume. */
|
|
32
|
-
readonly agentOptions?: () => AgentOptions;
|
|
33
|
-
/**
|
|
34
|
-
* Build the Host-specific Agent-scope composition completed before
|
|
35
|
-
* publication. Keyed by the resumed session itself because what a Host
|
|
36
|
-
* installs may depend on what that session recorded: an agent preset fixes
|
|
37
|
-
* the tools its history was produced under, so rebuilding it under another
|
|
38
|
-
* composition would replay tool calls the agent can no longer make. The
|
|
39
|
-
* events come along because a session's own record of such a choice may be
|
|
40
|
-
* an event rather than a header field.
|
|
41
|
-
* @param session - the resumed session's persisted header and event log.
|
|
42
|
-
* @returns the Agent-scope setup to run before publication.
|
|
43
|
-
*/
|
|
44
|
-
readonly setup?: (session: {
|
|
45
|
-
meta: SessionHeader;
|
|
46
|
-
events: readonly SessionEvent[];
|
|
47
|
-
}) => AgentSetup | Promise<AgentSetup>;
|
|
48
|
-
}
|
|
49
|
-
/** Cold identity absent from the durable session store. */
|
|
50
|
-
export declare class ApiRemoteSessionNotFound extends Error {
|
|
51
|
-
}
|
|
52
|
-
/** Session identity whose lifecycle belongs to subagent routing. */
|
|
53
|
-
export declare class ApiRemoteSubagentSessionOwnership extends Error {
|
|
54
|
-
readonly sessionId: SessionId;
|
|
55
|
-
/**
|
|
56
|
-
* Construct the ownership fence.
|
|
57
|
-
* @param sessionId - identity reserved to subagent routing.
|
|
58
|
-
*/
|
|
59
|
-
constructor(sessionId: SessionId);
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Test whether generic Host routing must leave an identity to subagent routing.
|
|
63
|
-
* @param ctx - Host Context carrying the live Agent registry.
|
|
64
|
-
* @param session - attached or live Session metadata.
|
|
65
|
-
* @param agent - live Agent when one is registered.
|
|
66
|
-
* @returns whether generic Remote and legacy API calls must reject the identity.
|
|
67
|
-
*/
|
|
68
|
-
export declare function hasApiRemoteSubagentOwner(ctx: Context, session: Pick<Session, 'header'>, agent: Agent | undefined): boolean;
|
|
69
|
-
/**
|
|
70
|
-
* Build the stable caller-facing ownership rejection.
|
|
71
|
-
* @param sessionId - identity reserved to subagent routing.
|
|
72
|
-
* @returns the existing `agent-busy` RPC shape.
|
|
73
|
-
*/
|
|
74
|
-
export declare function apiRemoteSubagentOwnershipError(sessionId: SessionId): ApiRemoteLookupError;
|
|
75
|
-
/**
|
|
76
|
-
* Inspect one cold served session without repairing, resuming, or publishing it.
|
|
77
|
-
* @param ctx - Host Context carrying the optional persistence provider.
|
|
78
|
-
* @param sessionId - durable identity to inspect.
|
|
79
|
-
* @returns detached metadata and events for a servable session.
|
|
80
|
-
* @throws {@link ApiRemoteSessionNotFound} when the identity has no project-backed session.
|
|
81
|
-
*/
|
|
82
|
-
export declare function inspectApiRemoteSession(ctx: Context, sessionId: SessionId): Promise<{
|
|
83
|
-
meta: SessionHeader;
|
|
84
|
-
events: SessionEvent[];
|
|
85
|
-
}>;
|
|
86
|
-
/**
|
|
87
|
-
* Create the Host's shared Agent resolver and configure Agent/Session Typert lookups.
|
|
88
|
-
* Live Agents are reused, ordinary cold sessions resume once per identity, and
|
|
89
|
-
* subagent-owned identities retain the legacy `agent-busy` fence.
|
|
90
|
-
* @param ctx - owning Host Context.
|
|
91
|
-
* @param options - defaults and Agent-scope setup used only for cold resume.
|
|
92
|
-
* @returns resolver shared by legacy API Proxy methods and Typert lookups.
|
|
93
|
-
*/
|
|
94
|
-
export declare function createApiRemoteAgentResolver(ctx: Context, options: ApiRemoteAgentOptions): (sessionId: SessionId) => Promise<ApiRemoteAgentResult>;
|
|
95
|
-
//# sourceMappingURL=agent-lookup.d.ts.map
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
/** Host BFF policy for resolving Remote Agent and Session identities. */
|
|
2
|
-
import { TypertLookupFailure } from '@deepseek-ai/dsh-typert-protocol';
|
|
3
|
-
/** Cold identity absent from the durable session store. */
|
|
4
|
-
export class ApiRemoteSessionNotFound extends Error {
|
|
5
|
-
}
|
|
6
|
-
/** Session identity whose lifecycle belongs to subagent routing. */
|
|
7
|
-
export class ApiRemoteSubagentSessionOwnership extends Error {
|
|
8
|
-
sessionId;
|
|
9
|
-
/**
|
|
10
|
-
* Construct the ownership fence.
|
|
11
|
-
* @param sessionId - identity reserved to subagent routing.
|
|
12
|
-
*/
|
|
13
|
-
constructor(sessionId) {
|
|
14
|
-
super(`session "${sessionId}" is a subagent session; use subagent delivery`);
|
|
15
|
-
this.sessionId = sessionId;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Test whether generic Host routing must leave an identity to subagent routing.
|
|
20
|
-
* @param ctx - Host Context carrying the live Agent registry.
|
|
21
|
-
* @param session - attached or live Session metadata.
|
|
22
|
-
* @param agent - live Agent when one is registered.
|
|
23
|
-
* @returns whether generic Remote and legacy API calls must reject the identity.
|
|
24
|
-
*/
|
|
25
|
-
export function hasApiRemoteSubagentOwner(ctx, session, agent) {
|
|
26
|
-
if (session.header.origin === 'subagent')
|
|
27
|
-
return true;
|
|
28
|
-
const parentId = session.header.parentSession;
|
|
29
|
-
if (parentId === undefined || agent === undefined)
|
|
30
|
-
return false;
|
|
31
|
-
const parent = ctx.agents.get(parentId);
|
|
32
|
-
return parent !== undefined && ctx.agents.isOwnedBy(agent.id, parent);
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Build the stable caller-facing ownership rejection.
|
|
36
|
-
* @param sessionId - identity reserved to subagent routing.
|
|
37
|
-
* @returns the existing `agent-busy` RPC shape.
|
|
38
|
-
*/
|
|
39
|
-
export function apiRemoteSubagentOwnershipError(sessionId) {
|
|
40
|
-
return {
|
|
41
|
-
code: 'agent-busy',
|
|
42
|
-
message: `session "${sessionId}" is owned by subagent routing`,
|
|
43
|
-
details: { reason: 'use subagent delivery for this child session' },
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Inspect one cold served session without repairing, resuming, or publishing it.
|
|
48
|
-
* @param ctx - Host Context carrying the optional persistence provider.
|
|
49
|
-
* @param sessionId - durable identity to inspect.
|
|
50
|
-
* @returns detached metadata and events for a servable session.
|
|
51
|
-
* @throws {@link ApiRemoteSessionNotFound} when the identity has no project-backed session.
|
|
52
|
-
*/
|
|
53
|
-
export async function inspectApiRemoteSession(ctx, sessionId) {
|
|
54
|
-
const persistence = ctx.get('sessionPersistence');
|
|
55
|
-
if (persistence === undefined) {
|
|
56
|
-
throw new Error('session persistence is not configured (load a dsh-session-persistence backend)');
|
|
57
|
-
}
|
|
58
|
-
const meta = (await persistence.list()).find(candidate => candidate.id === sessionId);
|
|
59
|
-
if (meta === undefined || meta.cwd === undefined) {
|
|
60
|
-
throw new ApiRemoteSessionNotFound(`session "${sessionId}" not found`);
|
|
61
|
-
}
|
|
62
|
-
const inspected = await persistence.inspect(sessionId);
|
|
63
|
-
if (inspected.meta.cwd === undefined) {
|
|
64
|
-
throw new ApiRemoteSessionNotFound(`session "${sessionId}" not found`);
|
|
65
|
-
}
|
|
66
|
-
return { meta: inspected.meta, events: [...inspected.events] };
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Create the Host's shared Agent resolver and configure Agent/Session Typert lookups.
|
|
70
|
-
* Live Agents are reused, ordinary cold sessions resume once per identity, and
|
|
71
|
-
* subagent-owned identities retain the legacy `agent-busy` fence.
|
|
72
|
-
* @param ctx - owning Host Context.
|
|
73
|
-
* @param options - defaults and Agent-scope setup used only for cold resume.
|
|
74
|
-
* @returns resolver shared by legacy API Proxy methods and Typert lookups.
|
|
75
|
-
*/
|
|
76
|
-
export function createApiRemoteAgentResolver(ctx, options) {
|
|
77
|
-
const resumes = new Map();
|
|
78
|
-
const fencedLiveAgent = (sessionId) => {
|
|
79
|
-
const live = ctx.agents.get(sessionId);
|
|
80
|
-
if (live === undefined)
|
|
81
|
-
return undefined;
|
|
82
|
-
if (hasApiRemoteSubagentOwner(ctx, live.session, live)) {
|
|
83
|
-
return { error: apiRemoteSubagentOwnershipError(sessionId) };
|
|
84
|
-
}
|
|
85
|
-
return { agent: live };
|
|
86
|
-
};
|
|
87
|
-
const agentFor = async (sessionId) => {
|
|
88
|
-
const fenced = fencedLiveAgent(sessionId);
|
|
89
|
-
if (fenced !== undefined)
|
|
90
|
-
return fenced;
|
|
91
|
-
const attached = ctx.sessions.get(sessionId);
|
|
92
|
-
if (attached !== undefined && hasApiRemoteSubagentOwner(ctx, attached, undefined)) {
|
|
93
|
-
return { error: apiRemoteSubagentOwnershipError(sessionId) };
|
|
94
|
-
}
|
|
95
|
-
let resume = resumes.get(sessionId);
|
|
96
|
-
if (resume === undefined) {
|
|
97
|
-
resume = (async () => {
|
|
98
|
-
try {
|
|
99
|
-
const inspected = await inspectApiRemoteSession(ctx, sessionId);
|
|
100
|
-
if (hasApiRemoteSubagentOwner(ctx, { header: inspected.meta }, undefined)) {
|
|
101
|
-
throw new ApiRemoteSubagentSessionOwnership(sessionId);
|
|
102
|
-
}
|
|
103
|
-
// Built from the inspected session before the published re-checks
|
|
104
|
-
// below, so those stay adjacent to `resume` and a Host setup that
|
|
105
|
-
// awaits (composing a preset, say) does not widen the collision
|
|
106
|
-
// window.
|
|
107
|
-
const setup = options.setup === undefined ? undefined : await options.setup(inspected);
|
|
108
|
-
const publishedSession = ctx.sessions.get(sessionId);
|
|
109
|
-
const publishedAgent = ctx.agents.get(sessionId);
|
|
110
|
-
if (publishedSession !== undefined
|
|
111
|
-
&& hasApiRemoteSubagentOwner(ctx, publishedSession, publishedAgent)) {
|
|
112
|
-
throw new ApiRemoteSubagentSessionOwnership(sessionId);
|
|
113
|
-
}
|
|
114
|
-
const handle = await ctx.agents.resume({
|
|
115
|
-
resumeSessionId: sessionId,
|
|
116
|
-
...options.agentOptions === undefined ? {} : { agentOptions: options.agentOptions() },
|
|
117
|
-
...setup === undefined ? {} : { setup },
|
|
118
|
-
});
|
|
119
|
-
return handle.agent;
|
|
120
|
-
}
|
|
121
|
-
finally {
|
|
122
|
-
resumes.delete(sessionId);
|
|
123
|
-
}
|
|
124
|
-
})();
|
|
125
|
-
resumes.set(sessionId, resume);
|
|
126
|
-
}
|
|
127
|
-
try {
|
|
128
|
-
return { agent: await resume };
|
|
129
|
-
}
|
|
130
|
-
catch (error) {
|
|
131
|
-
if (error instanceof ApiRemoteSessionNotFound) {
|
|
132
|
-
return { error: { code: 'session-not-found', message: error.message, details: { sessionId } } };
|
|
133
|
-
}
|
|
134
|
-
if (error instanceof ApiRemoteSubagentSessionOwnership) {
|
|
135
|
-
return { error: apiRemoteSubagentOwnershipError(error.sessionId) };
|
|
136
|
-
}
|
|
137
|
-
const fenced = fencedLiveAgent(sessionId);
|
|
138
|
-
if (fenced !== undefined)
|
|
139
|
-
return fenced;
|
|
140
|
-
const attached = ctx.sessions.get(sessionId);
|
|
141
|
-
if (attached !== undefined && hasApiRemoteSubagentOwner(ctx, attached, undefined)) {
|
|
142
|
-
return { error: apiRemoteSubagentOwnershipError(sessionId) };
|
|
143
|
-
}
|
|
144
|
-
return {
|
|
145
|
-
error: {
|
|
146
|
-
code: 'internal',
|
|
147
|
-
message: `resume failed for session "${sessionId}": ${String(error)}`,
|
|
148
|
-
details: {},
|
|
149
|
-
},
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
ctx.inject(['typert'], (typeCtx) => {
|
|
154
|
-
const resolveAgent = async (sessionId) => {
|
|
155
|
-
const found = await agentFor(sessionId);
|
|
156
|
-
if ('error' in found)
|
|
157
|
-
throw new TypertLookupFailure(found.error);
|
|
158
|
-
return found.agent;
|
|
159
|
-
};
|
|
160
|
-
typeCtx.typert.lookups.configure('agent', resolveAgent);
|
|
161
|
-
typeCtx.typert.lookups.configure('session', async (sessionId) => (await resolveAgent(sessionId)).session);
|
|
162
|
-
typeCtx.typert.contexts.configureHost('agent', async (sessionId) => (await resolveAgent(sessionId)).ctx);
|
|
163
|
-
});
|
|
164
|
-
return agentFor;
|
|
165
|
-
}
|
|
166
|
-
//# sourceMappingURL=agent-lookup.js.map
|