@khorsheed/dsh-local-agent 0.1.0-rc.6
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.en.md +181 -0
- package/README.i18n.yaml +2 -0
- package/README.md +181 -0
- package/cordis.patch.yml +21 -0
- package/lib/client.js +8832 -0
- package/lib/index.js +5281 -0
- package/lib/invariant.js +85 -0
- package/lib/member-bridge.js +298 -0
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +3137 -0
- package/lib/typert.remote-client.d.ts +67 -0
- package/lib/typert.remote-client.js +1971 -0
- package/lib/types/cli-version.d.ts +82 -0
- package/lib/types/cli-version.js +165 -0
- package/lib/types/client/AuthStatusDot.d.ts +17 -0
- package/lib/types/client/AuthStatusDot.js +9 -0
- package/lib/types/client/HarnessModelPicker.d.ts +17 -0
- package/lib/types/client/HarnessModelPicker.js +48 -0
- package/lib/types/client/LocalAgentRecordsAction.d.ts +41 -0
- package/lib/types/client/LocalAgentRecordsAction.js +74 -0
- package/lib/types/client/MemberComposer.d.ts +79 -0
- package/lib/types/client/MemberComposer.js +308 -0
- package/lib/types/client/MemberConfiguration.d.ts +9 -0
- package/lib/types/client/MemberConfiguration.js +46 -0
- package/lib/types/client/MemberInboxView.d.ts +12 -0
- package/lib/types/client/MemberInboxView.js +38 -0
- package/lib/types/client/MemberLiveNode.d.ts +14 -0
- package/lib/types/client/MemberLiveNode.js +74 -0
- package/lib/types/client/MemberPaintDiagnostics.d.ts +7 -0
- package/lib/types/client/MemberPaintDiagnostics.js +20 -0
- package/lib/types/client/ModelConfigurationFields.d.ts +15 -0
- package/lib/types/client/ModelConfigurationFields.js +23 -0
- package/lib/types/client/ProviderAuthBlock.d.ts +59 -0
- package/lib/types/client/ProviderAuthBlock.js +164 -0
- package/lib/types/client/auth-status.d.ts +28 -0
- package/lib/types/client/auth-status.js +56 -0
- package/lib/types/client/icons.d.ts +22 -0
- package/lib/types/client/icons.js +6 -0
- package/lib/types/client/index.d.ts +62 -0
- package/lib/types/client/index.js +135 -0
- package/lib/types/client/live-node.d.ts +14 -0
- package/lib/types/client/live-node.js +34 -0
- package/lib/types/client/live-output.d.ts +34 -0
- package/lib/types/client/live-output.js +94 -0
- package/lib/types/client/live-paint.d.ts +40 -0
- package/lib/types/client/live-paint.js +74 -0
- package/lib/types/client/locales.d.ts +120 -0
- package/lib/types/client/locales.js +227 -0
- package/lib/types/client/member-configuration.d.ts +41 -0
- package/lib/types/client/member-configuration.js +114 -0
- package/lib/types/client/member-dock.d.ts +94 -0
- package/lib/types/client/member-dock.js +102 -0
- package/lib/types/client/member-feed.d.ts +24 -0
- package/lib/types/client/member-feed.js +134 -0
- package/lib/types/client/request-id.d.ts +3 -0
- package/lib/types/client/request-id.js +6 -0
- package/lib/types/client/settings-scope.d.ts +74 -0
- package/lib/types/client/settings-scope.js +98 -0
- package/lib/types/client/use-configuration-menu.d.ts +10 -0
- package/lib/types/client/use-configuration-menu.js +61 -0
- package/lib/types/container.d.ts +69 -0
- package/lib/types/container.js +129 -0
- package/lib/types/env.d.ts +17 -0
- package/lib/types/env.js +89 -0
- package/lib/types/gateway.d.ts +162 -0
- package/lib/types/gateway.js +470 -0
- package/lib/types/index.d.ts +1463 -0
- package/lib/types/index.js +2704 -0
- package/lib/types/invariant.d.ts +16 -0
- package/lib/types/invariant.js +105 -0
- package/lib/types/live-flush.d.ts +21 -0
- package/lib/types/live-flush.js +59 -0
- package/lib/types/live-stream.d.ts +41 -0
- package/lib/types/live-stream.js +178 -0
- package/lib/types/member-bridge.d.ts +50 -0
- package/lib/types/member-bridge.js +216 -0
- package/lib/types/member-channel.d.ts +86 -0
- package/lib/types/member-channel.js +232 -0
- package/lib/types/member-control-storage.d.ts +10 -0
- package/lib/types/member-control-storage.js +100 -0
- package/lib/types/member-control.d.ts +62 -0
- package/lib/types/member-control.js +359 -0
- package/lib/types/member-controls.d.ts +23 -0
- package/lib/types/member-controls.js +143 -0
- package/lib/types/member-feed.d.ts +4 -0
- package/lib/types/member-feed.js +45 -0
- package/lib/types/member-inbox.d.ts +30 -0
- package/lib/types/member-inbox.js +225 -0
- package/lib/types/model-directory.d.ts +43 -0
- package/lib/types/model-directory.js +192 -0
- package/lib/types/prepared-members.d.ts +15 -0
- package/lib/types/prepared-members.js +61 -0
- package/lib/types/settings-face.d.ts +61 -0
- package/lib/types/settings-face.js +61 -0
- package/lib/types/types.d.ts +959 -0
- package/lib/types/types.js +24 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +112 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only Remote channel for the local-agent family: roster, per-harness
|
|
3
|
+
* auth status, and scoped session records. The web client polls these through
|
|
4
|
+
* Typert Gateway instead of the slash-command channel, so UI refreshes never
|
|
5
|
+
* leave `command/run`/`command/done` lifecycle nodes in the session log.
|
|
6
|
+
* Sign-out and login stay on the command channel: they are user-initiated
|
|
7
|
+
* actions whose visible command node is the expected feedback.
|
|
8
|
+
* @module @khorsheed/dsh-local-agent/gateway
|
|
9
|
+
*/
|
|
10
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
11
|
+
var useValue = arguments.length > 2;
|
|
12
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
13
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
14
|
+
}
|
|
15
|
+
return useValue ? value : void 0;
|
|
16
|
+
};
|
|
17
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
18
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
19
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
20
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
21
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
22
|
+
var _, done = false;
|
|
23
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
24
|
+
var context = {};
|
|
25
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
26
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
27
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
28
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
29
|
+
if (kind === "accessor") {
|
|
30
|
+
if (result === void 0) continue;
|
|
31
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
32
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
33
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
34
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
35
|
+
}
|
|
36
|
+
else if (_ = accept(result)) {
|
|
37
|
+
if (kind === "field") initializers.unshift(_);
|
|
38
|
+
else descriptor[key] = _;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
42
|
+
done = true;
|
|
43
|
+
};
|
|
44
|
+
import { randomUUID } from 'node:crypto';
|
|
45
|
+
import { SessionId } from '@deepseek-ai/dsh-session';
|
|
46
|
+
import { TypertRemoteService, Remote } from '@deepseek-ai/dsh-typert-protocol';
|
|
47
|
+
import { extendModelDirectory } from './model-directory.js';
|
|
48
|
+
import { mergeMemberFeeds } from './member-feed.js';
|
|
49
|
+
/**
|
|
50
|
+
* Remote-only projection of the local-agent registry, exposed to the browser
|
|
51
|
+
* through Typert Gateway under the `localAgentGateway` service key.
|
|
52
|
+
*/
|
|
53
|
+
/**
|
|
54
|
+
* Fold a model surface's `lastObserved` into its pick list: a member (or
|
|
55
|
+
* harness) whose only named model is what it was observed running still gets
|
|
56
|
+
* a one-item menu instead of an empty one. The observation appends LAST —
|
|
57
|
+
* the broker's own choices keep their ranking — and an already-listed value
|
|
58
|
+
* leaves the list untouched (choices arrive deduped from the broker).
|
|
59
|
+
*/
|
|
60
|
+
function withObservedChoice(info) {
|
|
61
|
+
const observed = info.lastObserved;
|
|
62
|
+
if (observed === undefined)
|
|
63
|
+
return info;
|
|
64
|
+
return {
|
|
65
|
+
...info,
|
|
66
|
+
choices: info.choices.includes(observed) ? info.choices : [...info.choices, observed],
|
|
67
|
+
...info.directory === undefined ? {} : { directory: extendModelDirectory(info.directory, [], [observed]) },
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
let LocalAgentGateway = (() => {
|
|
71
|
+
let _classSuper = TypertRemoteService;
|
|
72
|
+
let _instanceExtraInitializers = [];
|
|
73
|
+
let _followMemberOutput_decorators;
|
|
74
|
+
let _followMembers_decorators;
|
|
75
|
+
let _modelDirectory_decorators;
|
|
76
|
+
let _followModelDirectory_decorators;
|
|
77
|
+
let _memberDirectory_decorators;
|
|
78
|
+
let _followMemberDirectory_decorators;
|
|
79
|
+
let _roster_decorators;
|
|
80
|
+
let _activeDelegations_decorators;
|
|
81
|
+
let _status_decorators;
|
|
82
|
+
let _sessions_decorators;
|
|
83
|
+
let _memberOf_decorators;
|
|
84
|
+
let _harnessModel_decorators;
|
|
85
|
+
let _memberModel_decorators;
|
|
86
|
+
let _setMemberModel_decorators;
|
|
87
|
+
let _memberConfiguration_decorators;
|
|
88
|
+
let _selectMemberConfiguration_decorators;
|
|
89
|
+
let _cancelMemberConfiguration_decorators;
|
|
90
|
+
let _retryMemberConfiguration_decorators;
|
|
91
|
+
let _followMemberConfiguration_decorators;
|
|
92
|
+
let _promptMember_decorators;
|
|
93
|
+
let _memberInbox_decorators;
|
|
94
|
+
let _controlMemberInbox_decorators;
|
|
95
|
+
let _stopMember_decorators;
|
|
96
|
+
return class LocalAgentGateway extends _classSuper {
|
|
97
|
+
static {
|
|
98
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
99
|
+
_followMemberOutput_decorators = [Remote({ mode: 'stream' })];
|
|
100
|
+
_followMembers_decorators = [Remote({ mode: 'stream' })];
|
|
101
|
+
_modelDirectory_decorators = [Remote('modelDirectory')];
|
|
102
|
+
_followModelDirectory_decorators = [Remote({ mode: 'stream' })];
|
|
103
|
+
_memberDirectory_decorators = [Remote('memberDirectory')];
|
|
104
|
+
_followMemberDirectory_decorators = [Remote({ mode: 'stream' })];
|
|
105
|
+
_roster_decorators = [Remote('roster')];
|
|
106
|
+
_activeDelegations_decorators = [Remote('activeDelegations')];
|
|
107
|
+
_status_decorators = [Remote('status')];
|
|
108
|
+
_sessions_decorators = [Remote('sessions')];
|
|
109
|
+
_memberOf_decorators = [Remote('memberOf')];
|
|
110
|
+
_harnessModel_decorators = [Remote('harnessModel')];
|
|
111
|
+
_memberModel_decorators = [Remote('memberModel')];
|
|
112
|
+
_setMemberModel_decorators = [Remote('setMemberModel')];
|
|
113
|
+
_memberConfiguration_decorators = [Remote('memberConfiguration')];
|
|
114
|
+
_selectMemberConfiguration_decorators = [Remote('selectMemberConfiguration')];
|
|
115
|
+
_cancelMemberConfiguration_decorators = [Remote('cancelMemberConfiguration')];
|
|
116
|
+
_retryMemberConfiguration_decorators = [Remote('retryMemberConfiguration')];
|
|
117
|
+
_followMemberConfiguration_decorators = [Remote({ mode: 'stream' })];
|
|
118
|
+
_promptMember_decorators = [Remote('promptMember')];
|
|
119
|
+
_memberInbox_decorators = [Remote('memberInbox')];
|
|
120
|
+
_controlMemberInbox_decorators = [Remote('controlMemberInbox')];
|
|
121
|
+
_stopMember_decorators = [Remote('stopMember')];
|
|
122
|
+
__esDecorate(this, null, _followMemberOutput_decorators, { kind: "method", name: "followMemberOutput", static: false, private: false, access: { has: obj => "followMemberOutput" in obj, get: obj => obj.followMemberOutput }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
123
|
+
__esDecorate(this, null, _followMembers_decorators, { kind: "method", name: "followMembers", static: false, private: false, access: { has: obj => "followMembers" in obj, get: obj => obj.followMembers }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
124
|
+
__esDecorate(this, null, _modelDirectory_decorators, { kind: "method", name: "modelDirectory", static: false, private: false, access: { has: obj => "modelDirectory" in obj, get: obj => obj.modelDirectory }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
125
|
+
__esDecorate(this, null, _followModelDirectory_decorators, { kind: "method", name: "followModelDirectory", static: false, private: false, access: { has: obj => "followModelDirectory" in obj, get: obj => obj.followModelDirectory }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
126
|
+
__esDecorate(this, null, _memberDirectory_decorators, { kind: "method", name: "memberDirectory", static: false, private: false, access: { has: obj => "memberDirectory" in obj, get: obj => obj.memberDirectory }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
127
|
+
__esDecorate(this, null, _followMemberDirectory_decorators, { kind: "method", name: "followMemberDirectory", static: false, private: false, access: { has: obj => "followMemberDirectory" in obj, get: obj => obj.followMemberDirectory }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
128
|
+
__esDecorate(this, null, _roster_decorators, { kind: "method", name: "roster", static: false, private: false, access: { has: obj => "roster" in obj, get: obj => obj.roster }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
129
|
+
__esDecorate(this, null, _activeDelegations_decorators, { kind: "method", name: "activeDelegations", static: false, private: false, access: { has: obj => "activeDelegations" in obj, get: obj => obj.activeDelegations }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
130
|
+
__esDecorate(this, null, _status_decorators, { kind: "method", name: "status", static: false, private: false, access: { has: obj => "status" in obj, get: obj => obj.status }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
131
|
+
__esDecorate(this, null, _sessions_decorators, { kind: "method", name: "sessions", static: false, private: false, access: { has: obj => "sessions" in obj, get: obj => obj.sessions }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
132
|
+
__esDecorate(this, null, _memberOf_decorators, { kind: "method", name: "memberOf", static: false, private: false, access: { has: obj => "memberOf" in obj, get: obj => obj.memberOf }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
133
|
+
__esDecorate(this, null, _harnessModel_decorators, { kind: "method", name: "harnessModel", static: false, private: false, access: { has: obj => "harnessModel" in obj, get: obj => obj.harnessModel }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
134
|
+
__esDecorate(this, null, _memberModel_decorators, { kind: "method", name: "memberModel", static: false, private: false, access: { has: obj => "memberModel" in obj, get: obj => obj.memberModel }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
135
|
+
__esDecorate(this, null, _setMemberModel_decorators, { kind: "method", name: "setMemberModel", static: false, private: false, access: { has: obj => "setMemberModel" in obj, get: obj => obj.setMemberModel }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
136
|
+
__esDecorate(this, null, _memberConfiguration_decorators, { kind: "method", name: "memberConfiguration", static: false, private: false, access: { has: obj => "memberConfiguration" in obj, get: obj => obj.memberConfiguration }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
137
|
+
__esDecorate(this, null, _selectMemberConfiguration_decorators, { kind: "method", name: "selectMemberConfiguration", static: false, private: false, access: { has: obj => "selectMemberConfiguration" in obj, get: obj => obj.selectMemberConfiguration }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
138
|
+
__esDecorate(this, null, _cancelMemberConfiguration_decorators, { kind: "method", name: "cancelMemberConfiguration", static: false, private: false, access: { has: obj => "cancelMemberConfiguration" in obj, get: obj => obj.cancelMemberConfiguration }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
139
|
+
__esDecorate(this, null, _retryMemberConfiguration_decorators, { kind: "method", name: "retryMemberConfiguration", static: false, private: false, access: { has: obj => "retryMemberConfiguration" in obj, get: obj => obj.retryMemberConfiguration }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
140
|
+
__esDecorate(this, null, _followMemberConfiguration_decorators, { kind: "method", name: "followMemberConfiguration", static: false, private: false, access: { has: obj => "followMemberConfiguration" in obj, get: obj => obj.followMemberConfiguration }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
141
|
+
__esDecorate(this, null, _promptMember_decorators, { kind: "method", name: "promptMember", static: false, private: false, access: { has: obj => "promptMember" in obj, get: obj => obj.promptMember }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
142
|
+
__esDecorate(this, null, _memberInbox_decorators, { kind: "method", name: "memberInbox", static: false, private: false, access: { has: obj => "memberInbox" in obj, get: obj => obj.memberInbox }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
143
|
+
__esDecorate(this, null, _controlMemberInbox_decorators, { kind: "method", name: "controlMemberInbox", static: false, private: false, access: { has: obj => "controlMemberInbox" in obj, get: obj => obj.controlMemberInbox }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
144
|
+
__esDecorate(this, null, _stopMember_decorators, { kind: "method", name: "stopMember", static: false, private: false, access: { has: obj => "stopMember" in obj, get: obj => obj.stopMember }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
145
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
146
|
+
}
|
|
147
|
+
static inject = ['localAgent', 'sessions'];
|
|
148
|
+
constructor(ctx) {
|
|
149
|
+
super(ctx, 'localAgentGateway');
|
|
150
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Follow one member's transient output with a complete reconnect baseline.
|
|
154
|
+
* @param childSessionId - mirrored session identity.
|
|
155
|
+
* @param signal - Remote-owned cancellation, including browser disconnects.
|
|
156
|
+
* @returns bounded incremental updates; final content remains in session history.
|
|
157
|
+
*/
|
|
158
|
+
followMemberOutput(childSessionId, signal) {
|
|
159
|
+
return this.ctx.localAgent.liveStreams.follow(childSessionId, signal);
|
|
160
|
+
}
|
|
161
|
+
/** Shared browser stream: adding visible members does not consume more HTTP connections. */
|
|
162
|
+
followMembers(requests, signal) {
|
|
163
|
+
return mergeMemberFeeds(requests, (request, signal) => this.memberFeed(request, signal), signal);
|
|
164
|
+
}
|
|
165
|
+
async *memberFeed(request, signal) {
|
|
166
|
+
const memberId = request.memberId;
|
|
167
|
+
switch (request.channel) {
|
|
168
|
+
case 'output':
|
|
169
|
+
for await (const value of this.followMemberOutput(memberId, signal))
|
|
170
|
+
yield { memberId, channel: 'output', value };
|
|
171
|
+
break;
|
|
172
|
+
case 'configuration':
|
|
173
|
+
for await (const value of this.followMemberConfiguration(memberId, signal))
|
|
174
|
+
yield { memberId, channel: 'configuration', value };
|
|
175
|
+
break;
|
|
176
|
+
case 'directory':
|
|
177
|
+
for await (const value of this.followMemberDirectory(memberId, signal))
|
|
178
|
+
yield { memberId, channel: 'directory', value };
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
directoryBroker(name, childSessionId) {
|
|
183
|
+
const harness = this.ctx.localAgent.get(name);
|
|
184
|
+
if (childSessionId !== undefined) {
|
|
185
|
+
const record = this.ctx.localAgent.getDelegation(childSessionId);
|
|
186
|
+
if (record === undefined || record.provider !== harness?.delegationProvider)
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
return harness?.modelBroker;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Read or explicitly refresh native model discovery, without changing a member.
|
|
193
|
+
* @param name - owning harness.
|
|
194
|
+
* @param childSessionId - member context, or undefined for the harness default.
|
|
195
|
+
* @param refresh - bypass the discovery TTL.
|
|
196
|
+
* @returns source-labelled discovery state, or null for an unavailable broker.
|
|
197
|
+
*/
|
|
198
|
+
async modelDirectory(name, childSessionId, refresh) {
|
|
199
|
+
const broker = this.directoryBroker(name, childSessionId);
|
|
200
|
+
return await broker?.modelDirectory?.(childSessionId, refresh) ?? null;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Keep a currently open model picker synchronized with discovery completion.
|
|
204
|
+
* @param name - owning harness.
|
|
205
|
+
* @param childSessionId - member context, or undefined for the harness default.
|
|
206
|
+
* @param signal - Remote-owned cancellation.
|
|
207
|
+
* @returns directory baselines and subsequent snapshots.
|
|
208
|
+
*/
|
|
209
|
+
async *followModelDirectory(name, childSessionId, signal) {
|
|
210
|
+
const broker = this.directoryBroker(name, childSessionId);
|
|
211
|
+
if (broker?.followModelDirectory !== undefined)
|
|
212
|
+
yield* broker.followModelDirectory(childSessionId, signal);
|
|
213
|
+
else {
|
|
214
|
+
const directory = await broker?.modelDirectory?.(childSessionId);
|
|
215
|
+
if (directory !== undefined && !signal.aborted)
|
|
216
|
+
yield directory;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/** Resolve directory ownership from the recorded member, never a UI provider guess. */
|
|
220
|
+
async memberDirectory(childSessionId, refresh) {
|
|
221
|
+
const record = this.ctx.localAgent.getDelegation(childSessionId) ?? this.ctx.localAgent.memberBinding?.(childSessionId);
|
|
222
|
+
const broker = record === undefined ? undefined : this.ctx.localAgent.harnessForProvider(record.provider)?.modelBroker;
|
|
223
|
+
return await broker?.modelDirectory?.(childSessionId, refresh) ?? null;
|
|
224
|
+
}
|
|
225
|
+
async *followMemberDirectory(childSessionId, signal) {
|
|
226
|
+
const record = this.ctx.localAgent.getDelegation(childSessionId) ?? this.ctx.localAgent.memberBinding?.(childSessionId);
|
|
227
|
+
const broker = record === undefined ? undefined : this.ctx.localAgent.harnessForProvider(record.provider)?.modelBroker;
|
|
228
|
+
if (broker?.followModelDirectory !== undefined)
|
|
229
|
+
yield* broker.followModelDirectory(childSessionId, signal);
|
|
230
|
+
else {
|
|
231
|
+
const directory = await broker?.modelDirectory?.(childSessionId);
|
|
232
|
+
if (directory !== undefined && !signal.aborted)
|
|
233
|
+
yield directory;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Registered harnesses in registration order.
|
|
238
|
+
* @returns the roster rows.
|
|
239
|
+
*/
|
|
240
|
+
roster() {
|
|
241
|
+
return this.ctx.localAgent.roster();
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* The child session ids with an in-flight delegation run (the
|
|
245
|
+
* active-delegation registry's key set). Surfaces that render one-shot
|
|
246
|
+
* subagent rows poll this to mark a row as actually running — the official
|
|
247
|
+
* session summary's `running` flag is agent-based and stays false for
|
|
248
|
+
* external CLI one-shots, which have no live agent — and to offer the
|
|
249
|
+
* stop verb that dispatches `/local-agent stop <childSessionId>`.
|
|
250
|
+
* @returns the in-flight child session ids.
|
|
251
|
+
*/
|
|
252
|
+
activeDelegations() {
|
|
253
|
+
return this.ctx.localAgent.activeDelegations();
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* One harness's auth status, in the default scope or in a named one.
|
|
257
|
+
* @param name - the harness name.
|
|
258
|
+
* @param scope - the named scope to report on; absent means the default
|
|
259
|
+
* scope, so a client written before scopes existed gets exactly the
|
|
260
|
+
* snapshot it always got.
|
|
261
|
+
* @returns the status snapshot.
|
|
262
|
+
*/
|
|
263
|
+
status(name, scope) {
|
|
264
|
+
return this.ctx.localAgent.statusOf(name, scope);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* One harness's scoped session records, optionally narrowed to the sessions
|
|
268
|
+
* whose working directory is a given dsh session's cwd — the delegations a
|
|
269
|
+
* session's workspace actually produced, so one project's kimi sessions do
|
|
270
|
+
* not surface in another project's session.
|
|
271
|
+
* @param name - the harness name.
|
|
272
|
+
* @param sessionId - optional dsh session whose cwd filters the records.
|
|
273
|
+
* @returns the matching session records.
|
|
274
|
+
*/
|
|
275
|
+
async sessions(name, sessionId) {
|
|
276
|
+
const records = await this.ctx.localAgent.sessionsOf(name);
|
|
277
|
+
if (sessionId === undefined)
|
|
278
|
+
return records;
|
|
279
|
+
const cwd = this.ctx.sessions.get(SessionId(sessionId))?.header.cwd;
|
|
280
|
+
if (cwd === undefined)
|
|
281
|
+
return records;
|
|
282
|
+
return records.filter(record => record.workDir === cwd);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* The member channel's membership check: the delegation view for one dsh
|
|
286
|
+
* child session, or null when the family never delegated it. The composer
|
|
287
|
+
* chain elects on the session snapshot alone (one-shot subagent), then
|
|
288
|
+
* confirms membership through this Remote — a null answer renders the same
|
|
289
|
+
* read-only panel the official composer shows, never a writable box.
|
|
290
|
+
* The CLI-session resume handle (`cliSessionId`) deliberately stays off the
|
|
291
|
+
* wire: the resume facade resolves it host-side from the record.
|
|
292
|
+
* @param childSessionId - the dsh child session id being viewed.
|
|
293
|
+
* @returns the delegation view, or null for a non-member session.
|
|
294
|
+
*/
|
|
295
|
+
memberOf(childSessionId) {
|
|
296
|
+
const registry = this.ctx.localAgent;
|
|
297
|
+
const record = registry.getDelegation(childSessionId);
|
|
298
|
+
if (record === undefined)
|
|
299
|
+
return null;
|
|
300
|
+
const harness = registry.harnessForProvider(record.provider);
|
|
301
|
+
return {
|
|
302
|
+
childSessionId: record.childSessionId,
|
|
303
|
+
provider: record.provider,
|
|
304
|
+
parentSessionId: record.parentSessionId,
|
|
305
|
+
...harness === undefined ? {} : { harnessDisplayName: harness.displayName },
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* The model surface of one harness without a member — the settings card's
|
|
310
|
+
* "what would a round run with" read. Null when the harness is unknown or
|
|
311
|
+
* exposes no model broker: the card then keeps its free-text field exactly
|
|
312
|
+
* as before brokers existed. The core fills `lastObserved` from the
|
|
313
|
+
* delegation records (the harness's own provider) when the broker's answer
|
|
314
|
+
* carries none — display context for the cli-builtin layer, never a layer —
|
|
315
|
+
* and appends it to `choices` when no broker choice lists it, so the card's
|
|
316
|
+
* menu offers what last ran instead of staying empty.
|
|
317
|
+
* @param name - the harness name.
|
|
318
|
+
* @returns the layer-by-layer surface, or null.
|
|
319
|
+
*/
|
|
320
|
+
async harnessModel(name) {
|
|
321
|
+
const harness = this.ctx.localAgent.get(name);
|
|
322
|
+
if (harness?.modelBroker === undefined)
|
|
323
|
+
return null;
|
|
324
|
+
const info = await harness.modelBroker.modelInfo();
|
|
325
|
+
// A harness without a delegation provider has no records to observe from.
|
|
326
|
+
const observed = harness.delegationProvider === undefined
|
|
327
|
+
? undefined
|
|
328
|
+
: this.ctx.localAgent.latestObservedModel(harness.delegationProvider);
|
|
329
|
+
return withObservedChoice(info.lastObserved !== undefined || observed === undefined
|
|
330
|
+
? info
|
|
331
|
+
: { ...info, lastObserved: observed });
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* The model surface of one member — the composer's picker read. The
|
|
335
|
+
* delegation record's requested model rides in from the core so the broker
|
|
336
|
+
* can rank it between the override and the settings layer; the record's
|
|
337
|
+
* OBSERVED model fills `lastObserved` when the broker's answer carries none
|
|
338
|
+
* (a broker that names one itself always wins — spread order: the broker's
|
|
339
|
+
* answer first, the core's fill only over an absent field) and joins
|
|
340
|
+
* `choices` as the last entry when no broker choice lists it, so a member
|
|
341
|
+
* that ran once has a one-item pick list instead of an empty menu. Null for a
|
|
342
|
+
* non-member session or a brokerless harness.
|
|
343
|
+
* @param childSessionId - the dsh child session id of the member.
|
|
344
|
+
* @returns the layer-by-layer surface, or null.
|
|
345
|
+
*/
|
|
346
|
+
async memberModel(childSessionId) {
|
|
347
|
+
const registry = this.ctx.localAgent;
|
|
348
|
+
const observed = registry.getDelegation(childSessionId);
|
|
349
|
+
const record = observed ?? registry.memberBinding?.(childSessionId);
|
|
350
|
+
if (record === undefined)
|
|
351
|
+
return null;
|
|
352
|
+
const broker = registry.harnessForProvider(record.provider)?.modelBroker;
|
|
353
|
+
if (broker === undefined)
|
|
354
|
+
return null;
|
|
355
|
+
let info = await broker.modelInfo(childSessionId, record.model);
|
|
356
|
+
if (broker.configurationAdapter !== undefined) {
|
|
357
|
+
const configuration = registry.memberConfiguration(childSessionId);
|
|
358
|
+
info = { ...info, configuration, switchable: configuration.lockedReason === undefined };
|
|
359
|
+
delete info.reason;
|
|
360
|
+
if (configuration.lockedReason !== undefined)
|
|
361
|
+
info.reason = configuration.lockedReason;
|
|
362
|
+
if (configuration.current.resolved.model !== undefined)
|
|
363
|
+
info.effective = configuration.current.resolved.model;
|
|
364
|
+
}
|
|
365
|
+
return withObservedChoice(info.lastObserved !== undefined || observed?.observedModel === undefined
|
|
366
|
+
? info
|
|
367
|
+
: { ...info, lastObserved: observed?.observedModel });
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Switch a member's session-level model (the composer's picker write). The
|
|
371
|
+
* override outranks the delegation's recorded model and the settings
|
|
372
|
+
* layer; with the live driver on it retires the member's resident runtime
|
|
373
|
+
* so the NEXT round respawns onto the new model — the CLI session itself
|
|
374
|
+
* (its rollout) carries over. Failures arrive structured, never raw.
|
|
375
|
+
* @param childSessionId - the dsh child session id of the member.
|
|
376
|
+
* @param model - the model identifier, or undefined to clear the override
|
|
377
|
+
* (the member then follows the settings layer again).
|
|
378
|
+
* @returns `{ ok: true }` once applied, or a structured error.
|
|
379
|
+
*/
|
|
380
|
+
async setMemberModel(childSessionId, model) {
|
|
381
|
+
const registry = this.ctx.localAgent;
|
|
382
|
+
const record = registry.getDelegation(childSessionId);
|
|
383
|
+
if (record === undefined) {
|
|
384
|
+
return { ok: false, error: `localAgent: no delegation recorded for child session ${childSessionId}` };
|
|
385
|
+
}
|
|
386
|
+
const broker = registry.harnessForProvider(record.provider)?.modelBroker;
|
|
387
|
+
if (broker === undefined) {
|
|
388
|
+
return { ok: false, error: `localAgent: ${record.provider} exposes no model broker` };
|
|
389
|
+
}
|
|
390
|
+
try {
|
|
391
|
+
if (broker.configurationAdapter === undefined)
|
|
392
|
+
await broker.setMemberModel(childSessionId, model);
|
|
393
|
+
else
|
|
394
|
+
await registry.setMemberModel(childSessionId, model);
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
398
|
+
}
|
|
399
|
+
return { ok: true };
|
|
400
|
+
}
|
|
401
|
+
async memberConfiguration(childSessionId) {
|
|
402
|
+
return this.ctx.localAgent.memberConfiguration(childSessionId);
|
|
403
|
+
}
|
|
404
|
+
async selectMemberConfiguration(childSessionId, requestId, expectedRevision, selection) {
|
|
405
|
+
return this.ctx.localAgent.selectMemberConfiguration(childSessionId, requestId, expectedRevision, selection);
|
|
406
|
+
}
|
|
407
|
+
async cancelMemberConfiguration(childSessionId, requestId, expectedRevision) {
|
|
408
|
+
return this.ctx.localAgent.cancelMemberConfiguration(childSessionId, requestId, expectedRevision);
|
|
409
|
+
}
|
|
410
|
+
async retryMemberConfiguration(childSessionId, expectedRevision) {
|
|
411
|
+
await this.ctx.localAgent.retryMemberConfiguration(childSessionId, expectedRevision);
|
|
412
|
+
}
|
|
413
|
+
followMemberConfiguration(childSessionId, signal) {
|
|
414
|
+
return this.ctx.localAgent.followMemberConfiguration(childSessionId, signal);
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Send one human-authored follow-up to a family member: continue the
|
|
418
|
+
* member's SAME CLI session inside the SAME dsh child session. The child
|
|
419
|
+
* session id is the membership proof — the user opened the real child
|
|
420
|
+
* session, and the recorded delegation is the authorization source — so
|
|
421
|
+
* there is no model-forged handle surface; the resume handle never enters
|
|
422
|
+
* the prompt text. The prompt goes through the facade
|
|
423
|
+
* ({@link LocalAgentRegistry.resume}) unchanged: ownership check, resume
|
|
424
|
+
* lock, reattach, and run tracking all apply.
|
|
425
|
+
* @param childSessionId - the dsh child session id of the member.
|
|
426
|
+
* @param text - the human's follow-up message.
|
|
427
|
+
* @returns `{ ok: true }` once the run started, or a structured error
|
|
428
|
+
* (unknown member, parent not live, resume in flight) for the composer to
|
|
429
|
+
* render inline — raw exceptions never cross the wire.
|
|
430
|
+
*/
|
|
431
|
+
async promptMember(childSessionId, text, requestId) {
|
|
432
|
+
try {
|
|
433
|
+
return { ok: true, requestId: this.ctx.localAgent.enqueueMemberInput(childSessionId, text, requestId ?? randomUUID()) };
|
|
434
|
+
}
|
|
435
|
+
catch (error) {
|
|
436
|
+
return { ok: false, error: String(error) };
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
memberInbox(childSessionId) { return this.ctx.localAgent.readMemberInbox(childSessionId); }
|
|
440
|
+
controlMemberInbox(childSessionId, action, requestId, outcome, evidence) {
|
|
441
|
+
try {
|
|
442
|
+
this.ctx.localAgent.readMemberInbox(childSessionId);
|
|
443
|
+
const inbox = this.ctx.localAgent.memberInbox;
|
|
444
|
+
if (action === 'pause')
|
|
445
|
+
inbox.pause(childSessionId);
|
|
446
|
+
else if (action === 'resume')
|
|
447
|
+
this.ctx.localAgent.resumeMemberInbox(childSessionId);
|
|
448
|
+
else if (action === 'cancel' && requestId !== undefined)
|
|
449
|
+
inbox.cancel(childSessionId, requestId);
|
|
450
|
+
else if (action === 'reconcile' && requestId !== undefined && outcome !== undefined && evidence !== undefined)
|
|
451
|
+
inbox.reconcile(childSessionId, requestId, outcome, evidence);
|
|
452
|
+
else
|
|
453
|
+
throw new Error('Invalid member inbox control');
|
|
454
|
+
return { ok: true };
|
|
455
|
+
}
|
|
456
|
+
catch (error) {
|
|
457
|
+
return { ok: false, error: String(error) };
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Interrupt a member's in-flight run (the composer's Stop).
|
|
462
|
+
* @param childSessionId - the dsh child session id of the member.
|
|
463
|
+
* @returns whether an in-flight run was found and signalled.
|
|
464
|
+
*/
|
|
465
|
+
stopMember(childSessionId) {
|
|
466
|
+
return this.ctx.localAgent.cancel(childSessionId);
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
})();
|
|
470
|
+
export default LocalAgentGateway;
|