@wowyuarm/dsh-agent-team 0.1.14 → 0.1.15
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.md +2 -2
- package/README.zh.md +2 -2
- package/cordis.patch.yml +187 -2
- package/package.json +49 -50
- package/packages/agent-team/README.md +1 -1
- package/packages/agent-team/README.zh.md +1 -1
- package/packages/agent-team/lib/context-continuity-host.js +2 -3
- package/packages/agent-team/lib/context-projection.js +3 -3
- package/packages/agent-team/lib/context-source.js +34 -13
- package/packages/agent-team/lib/human-profile.js +123 -12
- package/packages/agent-team/lib/index.js +165 -99
- package/packages/agent-team/lib/member-context.js +5 -3
- package/packages/agent-team/lib/member-time-context.js +5 -2
- package/packages/agent-team/lib/pressure-policy.js +5 -3
- package/packages/agent-team/lib/stored-session-reader.js +1 -2
- package/packages/agent-team/lib/typert.host.js +394 -252
- package/packages/agent-team/lib/typert.remote-client.d.ts +6 -3
- package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
- package/packages/agent-team/lib/typert.remote-client.js +322 -223
- package/packages/agent-team/lib/types/context-continuity-host.d.ts.map +1 -1
- package/packages/agent-team/lib/types/context-source.d.ts +46 -13
- package/packages/agent-team/lib/types/context-source.d.ts.map +1 -1
- package/packages/agent-team/lib/types/human-profile.d.ts +83 -11
- package/packages/agent-team/lib/types/human-profile.d.ts.map +1 -1
- package/packages/agent-team/lib/types/index.d.ts +64 -26
- package/packages/agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/types/member-context.d.ts +14 -0
- package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
- package/packages/agent-team/lib/types/member-time-context.d.ts +15 -0
- package/packages/agent-team/lib/types/member-time-context.d.ts.map +1 -1
- package/packages/agent-team/lib/types/pressure-policy.d.ts.map +1 -1
- package/packages/agent-team/lib/types/stored-session-reader.d.ts +1 -2
- package/packages/agent-team/lib/types/stored-session-reader.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/entities.d.ts +3 -3
- package/packages/agent-team/lib/types/types/requests-results.d.ts +18 -0
- package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
- package/packages/client-agent-team/README.md +1 -1
- package/packages/client-agent-team/README.zh.md +1 -1
- package/packages/client-agent-team/lib/client.js +330 -239
- package/packages/client-agent-team/lib/client.js.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +6 -6
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +3 -3
- package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +4 -4
- package/packages/client-agent-team/lib/types/client/TeamComposer.js +3 -3
- package/packages/client-agent-team/lib/types/client/TeamFooterAction.js +2 -2
- package/packages/client-agent-team/lib/types/client/TeamMemberEditor.js +3 -3
- package/packages/client-agent-team/lib/types/client/TeamMembersAction.js +2 -2
- package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamMessage.js +1 -1
- package/packages/client-agent-team/lib/types/client/TeamRowMenu.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamRowMenu.js +2 -2
- package/packages/client-agent-team/lib/types/client/TeamSidebarSection.js +2 -2
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +3 -3
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +3 -3
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceSelector.js +2 -2
- package/packages/client-agent-team/lib/types/client/human-identity.d.ts +4 -8
- package/packages/client-agent-team/lib/types/client/human-identity.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/human-identity.js +0 -21
- package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/index.js +50 -49
- package/packages/client-agent-team/lib/types/client/multi-menu-field.js +2 -2
- package/packages/client-agent-team/lib/types/client/slots.d.ts +1 -1
- package/packages/tool-agent-team/README.md +1 -1
- package/packages/tool-agent-team/README.zh.md +1 -1
- package/packages/agent-team/lib/preset-roster.js +0 -15
- package/packages/agent-team/lib/session-remediation.js +0 -493
- package/packages/agent-team/lib/types/preset-roster.d.ts +0 -5
- package/packages/agent-team/lib/types/preset-roster.d.ts.map +0 -1
- package/packages/agent-team/lib/types/session-remediation.d.ts +0 -172
- package/packages/agent-team/lib/types/session-remediation.d.ts.map +0 -1
- package/packages/agent-team/preset/team-member/agent.cordis.yml +0 -166
|
@@ -1,33 +1,79 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
1
4
|
import z from '@deepseek-ai/schemastery';
|
|
5
|
+
import { parse } from 'yaml';
|
|
2
6
|
/**
|
|
3
7
|
* Human identity profile: the one configurable display name plus the avatar
|
|
4
8
|
* reference. `member:human` stays the durable identity everywhere; only the
|
|
5
9
|
* handle shown in team_view, @ matching, and UI follows this profile.
|
|
6
10
|
*
|
|
7
11
|
* Storage split (see spec.md v1):
|
|
8
|
-
* - name + avatarRef
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
12
|
+
* - name + avatarRef are the Team Host row's own Config, so they live in the
|
|
13
|
+
* active profile's patch document as that row's `config` and the settings
|
|
14
|
+
* service derives their form from this schema. rc.1 derives every form from
|
|
15
|
+
* a plugin's Config, so the retired `installSection` namespace — a section of
|
|
16
|
+
* its own — has no counterpart; the row id below is the settings namespace.
|
|
17
|
+
* - avatar bytes live under a persistent directory below; the profile holds
|
|
18
|
+
* only the reference, never a data URL. The composer attachment cache is
|
|
19
|
+
* TTL-bound and must not hold avatar bytes.
|
|
13
20
|
*/
|
|
14
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Settings namespace of the Human profile: the Team Host row's id in
|
|
23
|
+
* `cordis.patch.yml`, which is what the settings service addresses a form and a
|
|
24
|
+
* write by AND the id the profile-document write patches. It is addressed by
|
|
25
|
+
* this constant, never by the running Host's `ctx.fiber.entry`: a Remote call
|
|
26
|
+
* runs under its caller's context, so that lookup names the RPC gateway's row.
|
|
27
|
+
* `shipping.spec.ts` pins the constant to the row the composition declares.
|
|
28
|
+
*/
|
|
29
|
+
export const HUMAN_PROFILE_SETTINGS_NAMESPACE = 'wowyuarm-agent-team-host';
|
|
15
30
|
/** Fallback display name before any user override is stored. */
|
|
16
31
|
export const HUMAN_PROFILE_DEFAULT_NAME = 'human';
|
|
17
32
|
/** Repository home for the version footnote link. */
|
|
18
33
|
export const HUMAN_PROFILE_REPO_URL = 'https://github.com/wowyuarm/dsh-agent-team';
|
|
19
34
|
/**
|
|
20
|
-
* Bundle version shown in the settings footnote
|
|
21
|
-
*
|
|
35
|
+
* Bundle version shown in the settings footnote, and the current side of the
|
|
36
|
+
* update check: the version of the package THIS Host runs from — read from the
|
|
37
|
+
* installed manifest, so a `link:` checkout under the development profile and a
|
|
38
|
+
* registry tarball under stable each state their own truth. It is not a
|
|
39
|
+
* hand-maintained string: the 0.1.14 bundle shipped with `0.1.13` written in
|
|
40
|
+
* it, which made the footnote name the previous release and the update check
|
|
41
|
+
* offer the release the user already had.
|
|
42
|
+
*
|
|
43
|
+
* Resolved once at load, three levels above this module — `packages/agent-team/{src,lib}`
|
|
44
|
+
* sits that deep in both layouts, the same relative positioning
|
|
45
|
+
* `member-runtime.ts` uses to find `core-skills`. An unreadable or malformed
|
|
46
|
+
* manifest degrades to `'unknown'`: the Remote's `version: string` contract
|
|
47
|
+
* holds and the update comparison simply compares nothing. The footnote is
|
|
22
48
|
* informational only and never gates behavior.
|
|
23
49
|
*/
|
|
24
|
-
export const HUMAN_PROFILE_VERSION =
|
|
25
|
-
/**
|
|
50
|
+
export const HUMAN_PROFILE_VERSION = readInstalledBundleVersion();
|
|
51
|
+
/** Version of the manifest this package installed from, or `'unknown'`. */
|
|
52
|
+
function readInstalledBundleVersion() {
|
|
53
|
+
try {
|
|
54
|
+
const manifestPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../../package.json');
|
|
55
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
56
|
+
if (typeof manifest.version === 'string' && manifest.version !== '')
|
|
57
|
+
return manifest.version;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Reading our own manifest must never fail the Host boot: a host that
|
|
61
|
+
// cannot find its own package.json is a broken install, and the footnote
|
|
62
|
+
// reporting 'unknown' says so more honestly than a stale number.
|
|
63
|
+
}
|
|
64
|
+
return 'unknown';
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Schemastery schema of the Human profile: the Team Host row's Config. Both
|
|
68
|
+
* fields are volatile, which is what lets an edit reach the running Host
|
|
69
|
+
* without remounting it — and what makes the settings service derive a form
|
|
70
|
+
* from this schema at all.
|
|
71
|
+
*/
|
|
26
72
|
export const HUMAN_PROFILE_SETTINGS_SCHEMA = z.object({
|
|
27
|
-
name: z.string().default(HUMAN_PROFILE_DEFAULT_NAME),
|
|
73
|
+
name: z.string().default(HUMAN_PROFILE_DEFAULT_NAME).volatile(),
|
|
28
74
|
// Schemastery fields are optional unless `.required()`: a missing avatarRef
|
|
29
75
|
// simply resolves absent, which the profile reads as "no custom avatar".
|
|
30
|
-
avatarRef: z.string(),
|
|
76
|
+
avatarRef: z.string().volatile(),
|
|
31
77
|
});
|
|
32
78
|
/** Normalize one candidate display name the way Member handles normalize. */
|
|
33
79
|
export function normalizeHumanName(raw) {
|
|
@@ -44,3 +90,68 @@ export function assertValidHumanName(raw) {
|
|
|
44
90
|
throw new Error('human name must not be empty');
|
|
45
91
|
return name;
|
|
46
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* The retired settings section this profile's facts lived in before rc.1.
|
|
95
|
+
* rc.1 derives every settings form from a plugin's Config, and its legacy
|
|
96
|
+
* `settings.yaml` importer maps a section to a composition entry id through a
|
|
97
|
+
* closed built-in list — this third-party section matches nothing there, so
|
|
98
|
+
* the import rejects it and the values survive only in the renamed document.
|
|
99
|
+
* This Host owns the section, so adopting what is left belongs here.
|
|
100
|
+
*/
|
|
101
|
+
export const LEGACY_HUMAN_PROFILE_SECTION = 'agent-team-human';
|
|
102
|
+
/**
|
|
103
|
+
* Read the legacy section out of one legacy settings document. An
|
|
104
|
+
* unparsable document or a missing section means "nothing to adopt" rather
|
|
105
|
+
* than an error — the file is retired input, not an authority — and each
|
|
106
|
+
* field is validated independently so one bad field never costs the other.
|
|
107
|
+
*/
|
|
108
|
+
export function parseLegacyHumanProfile(yamlText) {
|
|
109
|
+
let document;
|
|
110
|
+
try {
|
|
111
|
+
document = parse(yamlText);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
if (typeof document !== 'object' || document === null)
|
|
117
|
+
return undefined;
|
|
118
|
+
const section = document[LEGACY_HUMAN_PROFILE_SECTION];
|
|
119
|
+
if (typeof section !== 'object' || section === null)
|
|
120
|
+
return undefined;
|
|
121
|
+
const fields = section;
|
|
122
|
+
let name;
|
|
123
|
+
if (typeof fields.name === 'string') {
|
|
124
|
+
try {
|
|
125
|
+
name = assertValidHumanName(fields.name);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
name = undefined;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const avatarRef = typeof fields.avatarRef === 'string' && fields.avatarRef.trim() !== ''
|
|
132
|
+
? fields.avatarRef.trim()
|
|
133
|
+
: undefined;
|
|
134
|
+
if (name === undefined && avatarRef === undefined)
|
|
135
|
+
return undefined;
|
|
136
|
+
return { ...(name === undefined ? {} : { name }), ...(avatarRef === undefined ? {} : { avatarRef }) };
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Decide what adoption may write: nothing unless the stored profile is still
|
|
140
|
+
* the pristine default, so a value the Human re-entered after the upgrade
|
|
141
|
+
* always wins, and never the legacy default name itself. The current profile
|
|
142
|
+
* carries explicit `undefined` on `avatarRef` — the Host getter spreads a
|
|
143
|
+
* settings read — and so is not `HumanProfile` under
|
|
144
|
+
* exactOptionalPropertyTypes; taking that shape directly keeps the Host call
|
|
145
|
+
* cast-free. The returned fields are exactly the ops the profile page would
|
|
146
|
+
* have written; byte existence for `avatarRef` is the caller's I/O and must
|
|
147
|
+
* already hold.
|
|
148
|
+
*/
|
|
149
|
+
export function planLegacyAdoption(current, legacy) {
|
|
150
|
+
if (current.name !== HUMAN_PROFILE_DEFAULT_NAME || current.avatarRef !== undefined)
|
|
151
|
+
return undefined;
|
|
152
|
+
const name = legacy.name !== undefined && legacy.name !== HUMAN_PROFILE_DEFAULT_NAME ? legacy.name : undefined;
|
|
153
|
+
const avatarRef = legacy.avatarRef;
|
|
154
|
+
if (name === undefined && avatarRef === undefined)
|
|
155
|
+
return undefined;
|
|
156
|
+
return { ...(name === undefined ? {} : { name }), ...(avatarRef === undefined ? {} : { avatarRef }) };
|
|
157
|
+
}
|
|
@@ -40,6 +40,8 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
|
|
|
40
40
|
done = true;
|
|
41
41
|
};
|
|
42
42
|
import { randomUUID } from 'node:crypto';
|
|
43
|
+
import { readFileSync } from 'node:fs';
|
|
44
|
+
import { join } from 'node:path';
|
|
43
45
|
import { isDeepStrictEqual } from 'node:util';
|
|
44
46
|
import { Service } from '@deepseek-ai/cordis';
|
|
45
47
|
import { installModelSelection } from '@deepseek-ai/dsh-agent';
|
|
@@ -50,18 +52,17 @@ import { Session, SessionId, SessionLogOffset } from '@deepseek-ai/dsh-session';
|
|
|
50
52
|
import { setSandboxMode } from '@deepseek-ai/dsh-sandbox-policy';
|
|
51
53
|
import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
52
54
|
import { ATTACHMENT_MAX_BYTES, attachmentPayloadPath, attachmentsRoot, copyPathAttachment, newAttachmentId, readAttachment, sanitizeMediaType, sweepAttachmentCache, validatePathAttachment, writeAttachment } from "./attachments.js";
|
|
53
|
-
import { HUMAN_PROFILE_DEFAULT_NAME, HUMAN_PROFILE_REPO_URL, HUMAN_PROFILE_SETTINGS_NAMESPACE, HUMAN_PROFILE_SETTINGS_SCHEMA, HUMAN_PROFILE_VERSION, assertValidHumanName, normalizeHumanName } from "./human-profile.js";
|
|
55
|
+
import { HUMAN_PROFILE_DEFAULT_NAME, HUMAN_PROFILE_REPO_URL, HUMAN_PROFILE_SETTINGS_NAMESPACE, HUMAN_PROFILE_SETTINGS_SCHEMA, HUMAN_PROFILE_VERSION, assertValidHumanName, normalizeHumanName, parseLegacyHumanProfile, planLegacyAdoption } from "./human-profile.js";
|
|
54
56
|
import { humanAvatarsRoot, readHumanAvatar, removeHumanAvatar, writeHumanAvatar } from "./human-avatar.js";
|
|
55
57
|
import { createHumanUpdateChecker } from "./human-update-check.js";
|
|
56
58
|
import { PressurePolicyCoordinator } from "./pressure-policy.js";
|
|
57
59
|
import { CONTEXT_CONTINUITY_PROJECTION_KEY, readContextTimeline } from '@wowyuarm/dsh-context-continuity';
|
|
58
60
|
import { createTeamContextManagement, TEAM_CONTEXT_CODEC } from "./context-continuity-host.js";
|
|
59
|
-
import { AGENT_TEAM_PLUGIN_ID } from "./context-source.js";
|
|
61
|
+
import { AGENT_TEAM_PLUGIN_ID, isAgentTeamSource } from "./context-source.js";
|
|
60
62
|
import { boundaryByRef, carriedInputOf, checkpointByRef, checkpointRefFor, createTeamContextProjectionConfig, createTeamContextProjectionDefinition, foldTeamContextProjection, retainedTopicsThrough, TeamContextProjectionHost } from "./context-projection.js";
|
|
61
63
|
import { AGENT_TEAM_HUMAN_MEMBER_ID, AgentTeamLedger, agentTeamHumanActor } from "./ledger.js";
|
|
62
64
|
import { AGENT_TEAM_TOOL_NAMES, deepCopyCapabilities, memberMemoryDirectoryName, MemberRuntime } from "./member-runtime.js";
|
|
63
65
|
import { classifyRecoverableError, RecoveryCoordinator, RECOVERY_MAX_CONSECUTIVE_ERRORS } from "./recovery.js";
|
|
64
|
-
import { SessionRemediation, handoffAlreadyInLog } from "./session-remediation.js";
|
|
65
66
|
import { StoredSessionReadError, StoredSessionReader, sessionFailureOf } from "./stored-session-reader.js";
|
|
66
67
|
import { agentTeamDomainSpec } from "./spec.js";
|
|
67
68
|
import { formatTeamTimestamp } from "./time-format.js";
|
|
@@ -98,7 +99,7 @@ const MAX_TIMELINE_LIMIT = 24;
|
|
|
98
99
|
* disagree about where history ends.
|
|
99
100
|
*/
|
|
100
101
|
const MAX_TIMELINE_ANCESTORS = 8;
|
|
101
|
-
/** Product pressure budget constants (see docs/team-collaboration.md). */
|
|
102
|
+
/** Product pressure budget constants (see docs/team-collaboration/README.md). */
|
|
102
103
|
const CONTEXT_HARD_LIMIT_CAP = 256_000;
|
|
103
104
|
const CONTEXT_HANDOFF_AT_CAP = 200_000;
|
|
104
105
|
const CONTEXT_HANDOFF_RESERVE = 8_000;
|
|
@@ -198,6 +199,7 @@ let AgentTeam = (() => {
|
|
|
198
199
|
let _putHumanAvatar_decorators;
|
|
199
200
|
let _getHumanAvatar_decorators;
|
|
200
201
|
let _removeHumanAvatar_decorators;
|
|
202
|
+
let _setHumanProfile_decorators;
|
|
201
203
|
let _reply_decorators;
|
|
202
204
|
let _changeAttention_decorators;
|
|
203
205
|
let _inbox_decorators;
|
|
@@ -233,6 +235,7 @@ let AgentTeam = (() => {
|
|
|
233
235
|
_putHumanAvatar_decorators = [Remote('putHumanAvatar')];
|
|
234
236
|
_getHumanAvatar_decorators = [Remote('getHumanAvatar')];
|
|
235
237
|
_removeHumanAvatar_decorators = [Remote('removeHumanAvatar')];
|
|
238
|
+
_setHumanProfile_decorators = [Remote('setHumanProfile')];
|
|
236
239
|
_reply_decorators = [Remote('reply')];
|
|
237
240
|
_changeAttention_decorators = [Remote('changeAttention')];
|
|
238
241
|
_inbox_decorators = [Remote('inbox')];
|
|
@@ -265,6 +268,7 @@ let AgentTeam = (() => {
|
|
|
265
268
|
__esDecorate(this, null, _putHumanAvatar_decorators, { kind: "method", name: "putHumanAvatar", static: false, private: false, access: { has: obj => "putHumanAvatar" in obj, get: obj => obj.putHumanAvatar }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
266
269
|
__esDecorate(this, null, _getHumanAvatar_decorators, { kind: "method", name: "getHumanAvatar", static: false, private: false, access: { has: obj => "getHumanAvatar" in obj, get: obj => obj.getHumanAvatar }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
267
270
|
__esDecorate(this, null, _removeHumanAvatar_decorators, { kind: "method", name: "removeHumanAvatar", static: false, private: false, access: { has: obj => "removeHumanAvatar" in obj, get: obj => obj.removeHumanAvatar }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
271
|
+
__esDecorate(this, null, _setHumanProfile_decorators, { kind: "method", name: "setHumanProfile", static: false, private: false, access: { has: obj => "setHumanProfile" in obj, get: obj => obj.setHumanProfile }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
268
272
|
__esDecorate(this, null, _reply_decorators, { kind: "method", name: "reply", static: false, private: false, access: { has: obj => "reply" in obj, get: obj => obj.reply }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
269
273
|
__esDecorate(this, null, _changeAttention_decorators, { kind: "method", name: "changeAttention", static: false, private: false, access: { has: obj => "changeAttention" in obj, get: obj => obj.changeAttention }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
270
274
|
__esDecorate(this, null, _inbox_decorators, { kind: "method", name: "inbox", static: false, private: false, access: { has: obj => "inbox" in obj, get: obj => obj.inbox }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
@@ -274,6 +278,8 @@ let AgentTeam = (() => {
|
|
|
274
278
|
__esDecorate(this, null, _view_decorators, { kind: "method", name: "view", static: false, private: false, access: { has: obj => "view" in obj, get: obj => obj.view }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
275
279
|
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
276
280
|
}
|
|
281
|
+
config = __runInitializers(this, _instanceExtraInitializers);
|
|
282
|
+
static Config = HUMAN_PROFILE_SETTINGS_SCHEMA;
|
|
277
283
|
static inject = [
|
|
278
284
|
'storageDomain',
|
|
279
285
|
'workspaceRegistry',
|
|
@@ -284,7 +290,7 @@ let AgentTeam = (() => {
|
|
|
284
290
|
'sessionPersistence',
|
|
285
291
|
'sessionProjections',
|
|
286
292
|
];
|
|
287
|
-
domain
|
|
293
|
+
domain;
|
|
288
294
|
ledger;
|
|
289
295
|
handles = new Map();
|
|
290
296
|
/** Live Member per session id; drives the root session/event listener. */
|
|
@@ -324,12 +330,6 @@ let AgentTeam = (() => {
|
|
|
324
330
|
pressurePolicy;
|
|
325
331
|
notifiedInbox = new Map();
|
|
326
332
|
attachmentGcTimer;
|
|
327
|
-
/**
|
|
328
|
-
* Current human profile source, wired to the `agent-team-human` settings
|
|
329
|
-
* namespace when a settings service is present. Falls back to the historic
|
|
330
|
-
* default so team_view and @ matching keep working without settings.
|
|
331
|
-
*/
|
|
332
|
-
humanProfileSource = () => ({ name: HUMAN_PROFILE_DEFAULT_NAME });
|
|
333
333
|
/**
|
|
334
334
|
* New-release check behind the settings footnote. Memory-only and
|
|
335
335
|
* background-refreshed, so the profile read path never waits on the
|
|
@@ -361,7 +361,7 @@ let AgentTeam = (() => {
|
|
|
361
361
|
* successful `context_rollover` tool result into its next private context
|
|
362
362
|
* generation. The ledger owns the binding audit, the engine's projection
|
|
363
363
|
* unit owns intent, and this coordinator owns only reconstructible process
|
|
364
|
-
* state. See docs/architecture.md and docs/team-collaboration.md.
|
|
364
|
+
* state. See docs/architecture/README.md and docs/team-collaboration/README.md.
|
|
365
365
|
*/
|
|
366
366
|
contextManagement = createTeamContextManagement({
|
|
367
367
|
agentForMember: memberId => this.handles.get(memberId)?.agent,
|
|
@@ -381,6 +381,8 @@ let AgentTeam = (() => {
|
|
|
381
381
|
});
|
|
382
382
|
lifecycleTail = Promise.resolve();
|
|
383
383
|
accepting = true;
|
|
384
|
+
/** One adoption attempt per boot: the two readiness edges fire once each, and this keeps their attempt single. */
|
|
385
|
+
legacyAdoptionStarted = false;
|
|
384
386
|
/**
|
|
385
387
|
* Presence wake epoch: Agent running/idle/failure is runtime state with no
|
|
386
388
|
* durable fact behind it, so the presence scope counts those edge wakes in
|
|
@@ -388,14 +390,9 @@ let AgentTeam = (() => {
|
|
|
388
390
|
*/
|
|
389
391
|
presenceEpoch = 0;
|
|
390
392
|
changeWaiters = new Set();
|
|
391
|
-
|
|
392
|
-
* The startup-opened remediation instance, held for the restart heal: the
|
|
393
|
-
* completion-cache domain may only be opened once per plugin lifecycle, so
|
|
394
|
-
* the restart path reuses this instance instead of opening its own.
|
|
395
|
-
*/
|
|
396
|
-
remediation;
|
|
397
|
-
constructor(ctx) {
|
|
393
|
+
constructor(ctx, config) {
|
|
398
394
|
super(ctx, 'agentTeam');
|
|
395
|
+
this.config = config;
|
|
399
396
|
this.pressurePolicy = new PressurePolicyCoordinator({
|
|
400
397
|
agentForMember: memberId => this.handles.get(memberId)?.agent,
|
|
401
398
|
memberForAgent: agent => {
|
|
@@ -412,37 +409,30 @@ let AgentTeam = (() => {
|
|
|
412
409
|
},
|
|
413
410
|
log: message => { this.ctx.logger.warn(`agent-team: ${message}`); },
|
|
414
411
|
});
|
|
415
|
-
// Human profile
|
|
416
|
-
// the settings
|
|
417
|
-
//
|
|
418
|
-
//
|
|
412
|
+
// Human profile: this Host row's own Config (name + avatarRef, both
|
|
413
|
+
// volatile), so the settings service derives its form from the schema and
|
|
414
|
+
// an edit lands in the running plugin without a remount. Team's own page
|
|
415
|
+
// owns that surface, so the row declares itself presentation-owned rather
|
|
416
|
+
// than schema-page owned. Reads go straight to the live Config reference;
|
|
417
|
+
// this subscription is the change signal the retired section hook used to
|
|
418
|
+
// be — the ledger's runtime @ handle follows every edit.
|
|
419
419
|
this.ctx.inject(['settings'], settingsCtx => {
|
|
420
|
-
settingsCtx.settings.
|
|
421
|
-
|
|
422
|
-
this.humanProfileSource = current;
|
|
423
|
-
this.syncHumanHandle();
|
|
424
|
-
},
|
|
425
|
-
validate: (value) => {
|
|
426
|
-
this.validateHumanProfile(value);
|
|
427
|
-
},
|
|
428
|
-
onChange: () => {
|
|
429
|
-
this.syncHumanHandle();
|
|
430
|
-
},
|
|
431
|
-
});
|
|
420
|
+
settingsCtx.effect(() => settingsCtx.settings.configure({ auto: false }, this.ctx.fiber));
|
|
421
|
+
this.adoptLegacyHumanProfile();
|
|
432
422
|
});
|
|
423
|
+
this.ctx.on('loader/volatile-update', () => { this.syncHumanHandle(); });
|
|
433
424
|
}
|
|
434
425
|
/** Current human display name; the single source for team_view and @ matching. */
|
|
435
426
|
humanHandle() {
|
|
436
|
-
const name = normalizeHumanName(this.
|
|
427
|
+
const name = normalizeHumanName(this.config.name.get());
|
|
437
428
|
return name === '' ? HUMAN_PROFILE_DEFAULT_NAME : name;
|
|
438
429
|
}
|
|
439
|
-
/** Current human profile reference held in
|
|
430
|
+
/** Current human profile reference held in this Host row's Config (name + avatarRef). */
|
|
440
431
|
humanProfile() {
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
return Object.freeze({ name, ...(current.avatarRef === undefined ? {} : { avatarRef: current.avatarRef }) });
|
|
432
|
+
const avatarRef = this.config.avatarRef.get();
|
|
433
|
+
return Object.freeze({ name: this.humanHandle(), ...(avatarRef === undefined ? {} : { avatarRef }) });
|
|
444
434
|
}
|
|
445
|
-
/** Push the current
|
|
435
|
+
/** Push the current Config name into the ledger's runtime @ handle. */
|
|
446
436
|
syncHumanHandle() {
|
|
447
437
|
try {
|
|
448
438
|
this.ledger?.setHumanDisplayHandle(this.humanHandle());
|
|
@@ -452,9 +442,10 @@ let AgentTeam = (() => {
|
|
|
452
442
|
}
|
|
453
443
|
}
|
|
454
444
|
/**
|
|
455
|
-
*
|
|
456
|
-
* Member handles plus global uniqueness against live
|
|
457
|
-
* the
|
|
445
|
+
* The two judgements the Config schema cannot make about a Human name: the
|
|
446
|
+
* same non-empty floor as Member handles, plus global uniqueness against live
|
|
447
|
+
* Members. `setHumanProfile` runs it before the write, so a colliding rename
|
|
448
|
+
* rejects instead of persisting.
|
|
458
449
|
*/
|
|
459
450
|
validateHumanProfile(value) {
|
|
460
451
|
const name = assertValidHumanName(value.name);
|
|
@@ -470,6 +461,73 @@ let AgentTeam = (() => {
|
|
|
470
461
|
}
|
|
471
462
|
}
|
|
472
463
|
}
|
|
464
|
+
/**
|
|
465
|
+
* One-time adoption of the profile facts the retired `agent-team-human`
|
|
466
|
+
* settings section held (see `LEGACY_HUMAN_PROFILE_SECTION` for why the
|
|
467
|
+
* upstream importer cannot carry them over). The values land in this Host
|
|
468
|
+
* row's own Config through the same Remote the profile page writes, so the
|
|
469
|
+
* page, the ledger's @ handle, and every avatar seat follow as after any
|
|
470
|
+
* edit. Readiness has two one-way edges — the settings service arrives, the
|
|
471
|
+
* ledger opens — and whichever fires second starts the attempt; only a
|
|
472
|
+
* pristine profile is adopted, and one attempt per boot is its whole
|
|
473
|
+
* lifetime, so adoption never loops and never re-writes.
|
|
474
|
+
*/
|
|
475
|
+
adoptLegacyHumanProfile() {
|
|
476
|
+
if (this.legacyAdoptionStarted)
|
|
477
|
+
return;
|
|
478
|
+
if (this.ctx.get('settings') === undefined || this.ledger === undefined)
|
|
479
|
+
return;
|
|
480
|
+
this.legacyAdoptionStarted = true;
|
|
481
|
+
void this.adoptLegacyHumanProfileNow().catch((error) => {
|
|
482
|
+
this.ctx.logger.warn('agent-team: legacy human profile adoption failed: %s', String(error));
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
/** Carry the first legacy document that still has the section into a pristine profile. */
|
|
486
|
+
async adoptLegacyHumanProfileNow() {
|
|
487
|
+
// A profile-launched Host always carries the profile context; a Host booted
|
|
488
|
+
// without one still adopts from the DSH home, which is where a pre-rc.1
|
|
489
|
+
// install kept the document. Read through `get`: cordis refuses a direct
|
|
490
|
+
// `ctx.profileContext` for a service this plugin does not declare, and
|
|
491
|
+
// adoption may not gate the Host on app-boot.
|
|
492
|
+
const profileHome = this.ctx.get('profileContext')?.home;
|
|
493
|
+
const homes = profileHome === undefined ? [dshHomePath()] : [dshHomePath(), profileHome];
|
|
494
|
+
const candidates = new Set(homes.flatMap(home => [join(home, 'settings.yaml'), join(home, 'settings.yaml.imported')]));
|
|
495
|
+
let legacy;
|
|
496
|
+
let source;
|
|
497
|
+
for (const candidate of candidates) {
|
|
498
|
+
let text;
|
|
499
|
+
try {
|
|
500
|
+
text = readFileSync(candidate, 'utf8');
|
|
501
|
+
}
|
|
502
|
+
catch {
|
|
503
|
+
// Absent candidates are the steady state once adoption settles; the
|
|
504
|
+
// live document is one only because the first boot after the upgrade
|
|
505
|
+
// can race the importer that renames it.
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
const parsed = parseLegacyHumanProfile(text);
|
|
509
|
+
if (parsed !== undefined) {
|
|
510
|
+
legacy = parsed;
|
|
511
|
+
source = candidate;
|
|
512
|
+
break;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
if (legacy === undefined || source === undefined)
|
|
516
|
+
return;
|
|
517
|
+
// The reference is carried only while its bytes still draw: the avatar
|
|
518
|
+
// store is the authority for whether this avatar exists at all.
|
|
519
|
+
let avatarRef = legacy.avatarRef;
|
|
520
|
+
if (avatarRef !== undefined && await readHumanAvatar(humanAvatarsRoot(), avatarRef) === undefined)
|
|
521
|
+
avatarRef = undefined;
|
|
522
|
+
const plan = planLegacyAdoption(this.humanProfile(), {
|
|
523
|
+
...(legacy.name === undefined ? {} : { name: legacy.name }),
|
|
524
|
+
...(avatarRef === undefined ? {} : { avatarRef }),
|
|
525
|
+
});
|
|
526
|
+
if (plan === undefined)
|
|
527
|
+
return;
|
|
528
|
+
await this.setHumanProfile(plan);
|
|
529
|
+
this.ctx.logger.info('agent-team: adopted the legacy human profile from %s', source);
|
|
530
|
+
}
|
|
473
531
|
/** Open the durable ledger and restore every enabled Member independently. */
|
|
474
532
|
async [Service.init]() {
|
|
475
533
|
// The continuity projection registers once per Host: the framework keeps
|
|
@@ -545,7 +603,6 @@ let AgentTeam = (() => {
|
|
|
545
603
|
const domain = await this.ctx.storageDomain.open(agentTeamDomainSpec);
|
|
546
604
|
this.ctx.effect(() => async () => {
|
|
547
605
|
this.accepting = false;
|
|
548
|
-
this.remediation = undefined;
|
|
549
606
|
this.recovery.dispose();
|
|
550
607
|
this.contextManagement.dispose();
|
|
551
608
|
this.pressurePolicy.dispose();
|
|
@@ -564,25 +621,15 @@ let AgentTeam = (() => {
|
|
|
564
621
|
this.domain = domain;
|
|
565
622
|
const ledger = new AgentTeamLedger(domain.table('operations'));
|
|
566
623
|
this.ledger = ledger;
|
|
567
|
-
//
|
|
568
|
-
// existed; sync once here so @ matching starts from the stored
|
|
624
|
+
// A live Config edit in the constructor's window may have arrived before
|
|
625
|
+
// the ledger existed; sync once here so @ matching starts from the stored
|
|
626
|
+
// name.
|
|
569
627
|
this.syncHumanHandle();
|
|
628
|
+
this.adoptLegacyHumanProfile();
|
|
570
629
|
const initialization = await ledger.initialize();
|
|
571
630
|
if (initialization.committed)
|
|
572
631
|
this.emitCommitted(initialization.value);
|
|
573
632
|
this.startAttachmentGc(ledger);
|
|
574
|
-
// Legacy-artifact remediation runs before any Member activation: no write
|
|
575
|
-
// lease exists yet, so publishing sibling generations for refused Session
|
|
576
|
-
// logs cannot race a live writer. Remediation failure never blocks
|
|
577
|
-
// startup — the next start retries exactly what the cache does not cover.
|
|
578
|
-
try {
|
|
579
|
-
const remediation = new SessionRemediation(this.ctx, this.ctx.sessionPersistence, await SessionRemediation.open(this.ctx));
|
|
580
|
-
this.remediation = remediation;
|
|
581
|
-
await remediation.remediateEnabledMembers(ledger.listMembers());
|
|
582
|
-
}
|
|
583
|
-
catch (error) {
|
|
584
|
-
this.ctx.logger.warn(`agent-team: legacy Session remediation did not run to completion (it will retry on the next start): ${error instanceof Error ? error.message : String(error)}`);
|
|
585
|
-
}
|
|
586
633
|
// One metadata listing serves every Member restore; per-member list calls
|
|
587
634
|
// would repeat the same I/O linearly during startup.
|
|
588
635
|
const persistedSessions = new Set((await this.persistedSessionHeaders()).map(snapshot => snapshot.header.id));
|
|
@@ -836,23 +883,10 @@ let AgentTeam = (() => {
|
|
|
836
883
|
if (member.state !== 'enabled')
|
|
837
884
|
throw new Error(`Agent Member '${member.handle}' is ${member.state}; only enabled Members can be restarted`);
|
|
838
885
|
this.ctx.logger.info(`agent-team: restarting member '${member.handle}' after a failed activation`);
|
|
839
|
-
//
|
|
840
|
-
//
|
|
841
|
-
//
|
|
842
|
-
|
|
843
|
-
if (activation !== undefined && activation.class === 'session-refused' && this.remediation !== undefined) {
|
|
844
|
-
const outcome = await this.remediation.remediateMember(member);
|
|
845
|
-
if (outcome.repaired > 0) {
|
|
846
|
-
this.ctx.logger.info(`agent-team: repaired ${outcome.repaired} refused Session artifact(s) for member '${member.handle}'; retrying activation`);
|
|
847
|
-
}
|
|
848
|
-
else if (outcome.completed) {
|
|
849
|
-
// The walk finished and nothing was provably this plugin's to fix:
|
|
850
|
-
// a retry would fail identically. Mark the refusal non-remediable
|
|
851
|
-
// so the surface stops offering restart and says why.
|
|
852
|
-
this.markRefusalNonRemediable(request.memberId);
|
|
853
|
-
return Object.freeze({ status: this.memberStatus(member) });
|
|
854
|
-
}
|
|
855
|
-
}
|
|
886
|
+
// There is no write-side repair pass anymore: dsh 0.1.7 converts the
|
|
887
|
+
// released V3 history at read time, and this bundle no longer authors
|
|
888
|
+
// the old wrapper shape, so a refusal stays a deterministic failure the
|
|
889
|
+
// retry reports again rather than something the restart heals.
|
|
856
890
|
await this.reactivateMember(request.memberId);
|
|
857
891
|
return Object.freeze({ status: this.memberStatus(member) });
|
|
858
892
|
}
|
|
@@ -1068,7 +1102,7 @@ let AgentTeam = (() => {
|
|
|
1068
1102
|
const body = notifications.length === 0 ? text : `${text}\n\n${this.notificationText(notifications, member.memberId)}`;
|
|
1069
1103
|
const hint = createUserMessage({
|
|
1070
1104
|
content: [{ type: 'text', text: body }],
|
|
1071
|
-
source: { kind:
|
|
1105
|
+
source: { kind: AGENT_TEAM_PLUGIN_ID, form: 'notice', summary: RECOVERY_NOTICE_SUMMARY },
|
|
1072
1106
|
});
|
|
1073
1107
|
for (const pending of [...handle.agent.inbox.nextStep, ...handle.agent.inbox.nextTurn]) {
|
|
1074
1108
|
if (this.isInboxNotice(pending))
|
|
@@ -1276,9 +1310,9 @@ let AgentTeam = (() => {
|
|
|
1276
1310
|
}
|
|
1277
1311
|
/**
|
|
1278
1312
|
* Human profile read for the settings page and footnote: name + avatar
|
|
1279
|
-
* reference from
|
|
1280
|
-
* Client calls it); agent tools never receive avatar bytes, only the
|
|
1281
|
-
* through team_view. The new-release check stays best-effort and cached —
|
|
1313
|
+
* reference from this Host row's live Config plus version facts. Human-scoped
|
|
1314
|
+
* (the Web Client calls it); agent tools never receive avatar bytes, only the
|
|
1315
|
+
* name through team_view. The new-release check stays best-effort and cached —
|
|
1282
1316
|
* `updateAvailable` is false until a background refresh actually observes a
|
|
1283
1317
|
* newer published release.
|
|
1284
1318
|
*/
|
|
@@ -1318,6 +1352,47 @@ let AgentTeam = (() => {
|
|
|
1318
1352
|
await removeHumanAvatar(humanAvatarsRoot(), request.avatarRef);
|
|
1319
1353
|
return Object.freeze({ removed: true });
|
|
1320
1354
|
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Overwrite the Human profile fields the caller supplies. The Host owns this
|
|
1357
|
+
* write because the profile is the Host row's own Config: the schema supplies
|
|
1358
|
+
* the shape, this method supplies the two judgements the schema cannot make
|
|
1359
|
+
* (a non-empty name, a name no live Member already answers to), and the
|
|
1360
|
+
* settings service persists the result into the active profile's patch
|
|
1361
|
+
* document and applies it to the running plugin live.
|
|
1362
|
+
*
|
|
1363
|
+
* No expected revision accompanies the write: the profile is two scalar
|
|
1364
|
+
* fields written from the Human's own pages, where the last write wins. This
|
|
1365
|
+
* is not a hard boundary around a user-editable document — the settings
|
|
1366
|
+
* service's own document opener (and a text editor) can change the stored
|
|
1367
|
+
* name without passing here, exactly as the retired section validator could
|
|
1368
|
+
* not stop it.
|
|
1369
|
+
*/
|
|
1370
|
+
async setHumanProfile(request) {
|
|
1371
|
+
this.requireAccepting();
|
|
1372
|
+
const settings = this.ctx.get('settings');
|
|
1373
|
+
if (settings === undefined)
|
|
1374
|
+
throw new Error('the settings service is unavailable');
|
|
1375
|
+
const name = request.name === undefined ? undefined : assertValidHumanName(request.name);
|
|
1376
|
+
if (name !== undefined)
|
|
1377
|
+
this.validateHumanProfile({ name });
|
|
1378
|
+
const ops = [];
|
|
1379
|
+
if (name !== undefined)
|
|
1380
|
+
ops.push({ op: 'set', path: ['name'], value: name });
|
|
1381
|
+
if (request.avatarRef === null)
|
|
1382
|
+
ops.push({ op: 'unset', path: ['avatarRef'] });
|
|
1383
|
+
else if (request.avatarRef !== undefined)
|
|
1384
|
+
ops.push({ op: 'set', path: ['avatarRef'], value: request.avatarRef });
|
|
1385
|
+
if (ops.length !== 0) {
|
|
1386
|
+
// The settings namespace IS this Host row's id in the composition, and it
|
|
1387
|
+
// must be addressed by that name rather than by `ctx.fiber.entry` here: a
|
|
1388
|
+
// Remote call runs under its caller's context, whose fiber entry is the
|
|
1389
|
+
// RPC gateway's row (`typert-gateway`), which the settings service then
|
|
1390
|
+
// looks up as an unrelated plugin. shipping.spec.ts pins the constant to
|
|
1391
|
+
// the row `cordis.patch.yml` declares.
|
|
1392
|
+
await settings.mutate(HUMAN_PROFILE_SETTINGS_NAMESPACE, ops);
|
|
1393
|
+
}
|
|
1394
|
+
return Object.freeze({ ...this.humanProfile() });
|
|
1395
|
+
}
|
|
1321
1396
|
/** Human existing-Thread reply; unread and revision conflicts are business outcomes. */
|
|
1322
1397
|
async reply(request) {
|
|
1323
1398
|
return this.replyAs(this.humanCall(request.workspaceId), request);
|
|
@@ -1483,7 +1558,7 @@ let AgentTeam = (() => {
|
|
|
1483
1558
|
try {
|
|
1484
1559
|
const message = createUserMessage({
|
|
1485
1560
|
content: [{ type: 'text', text: this.dmRelayText(agent, recipient, request.body.trim(), result.value.receipt.occurredAt, result.value.receipt.operationId, request.workspaceId) }],
|
|
1486
|
-
source: { kind:
|
|
1561
|
+
source: { kind: AGENT_TEAM_PLUGIN_ID, form: 'relay' },
|
|
1487
1562
|
});
|
|
1488
1563
|
// An idle recipient gets one ordinary turn; a busy one is steered into
|
|
1489
1564
|
// its current turn — the same wake split subagent continuations use.
|
|
@@ -2430,12 +2505,11 @@ let AgentTeam = (() => {
|
|
|
2430
2505
|
// delivered activates the new Session with no handoff in its own
|
|
2431
2506
|
// log — never treat that as an ordinary blank Member Session. The
|
|
2432
2507
|
// operation's recorded previous Session (the lineage parent) still
|
|
2433
|
-
// holds the intent; rebuild the handoff from it.
|
|
2434
|
-
//
|
|
2435
|
-
//
|
|
2436
|
-
//
|
|
2437
|
-
|
|
2438
|
-
if (!handoffAlreadyInLog(state.boundaries, created.agent.session.ownEvents())) {
|
|
2508
|
+
// holds the intent; rebuild the handoff from it. Presence is judged
|
|
2509
|
+
// by the projection boundary alone: every handoff this Host ever
|
|
2510
|
+
// published classifies under the context source's recognizer,
|
|
2511
|
+
// including the history the read-time conversion renamed.
|
|
2512
|
+
if (!state.boundaries.some(boundary => boundary.kind === 'handoff')) {
|
|
2439
2513
|
await this.reconstructMissingHandoff(member, created.agent);
|
|
2440
2514
|
}
|
|
2441
2515
|
// Carried input redelivery binds to the committed transition target —
|
|
@@ -2609,14 +2683,6 @@ let AgentTeam = (() => {
|
|
|
2609
2683
|
}
|
|
2610
2684
|
return { class: 'activation', detail: error instanceof Error ? error.message : String(error) };
|
|
2611
2685
|
}
|
|
2612
|
-
/** Mark a session-refused activation diagnostic as proven non-remediable. */
|
|
2613
|
-
markRefusalNonRemediable(memberId) {
|
|
2614
|
-
const failures = this.memberFailures.get(memberId);
|
|
2615
|
-
const activation = failures?.activation;
|
|
2616
|
-
if (activation === undefined || activation.class !== 'session-refused')
|
|
2617
|
-
return;
|
|
2618
|
-
failures.activation = Object.freeze({ ...activation, remediable: false });
|
|
2619
|
-
}
|
|
2620
2686
|
clearMemberFailure(memberId, slot) {
|
|
2621
2687
|
const failures = this.memberFailures.get(memberId);
|
|
2622
2688
|
if (failures === undefined || failures[slot] === undefined)
|
|
@@ -2657,7 +2723,7 @@ let AgentTeam = (() => {
|
|
|
2657
2723
|
const path = this.ctx.workspaceRegistry.get(operation.data.workspaceId)?.path;
|
|
2658
2724
|
const text = `Team participation changed: you have ${operation.kind === 'team/member-workspace-joined' ? 'joined' : 'left'} Workspace ${operation.data.workspaceId}${path === undefined ? ' (path unavailable)' : ` (${JSON.stringify(path)})`}.\nCurrent Workspace ids: ${ledger.workspacesOf(operation.data.memberId).join(', ')}. This replaces earlier participation information. Your Session and cwd have not moved.`;
|
|
2659
2725
|
const notice = createUserMessage({ content: [{ type: 'text', text }],
|
|
2660
|
-
source: { kind:
|
|
2726
|
+
source: { kind: AGENT_TEAM_PLUGIN_ID, form: 'notice', summary: 'Team Workspace participation changed' } });
|
|
2661
2727
|
try {
|
|
2662
2728
|
if (agent.status === 'idle' || agent.inbox.nextTurn.some(message => message.source.kind === 'user'))
|
|
2663
2729
|
agent.followup(notice);
|
|
@@ -2754,7 +2820,7 @@ let AgentTeam = (() => {
|
|
|
2754
2820
|
agent.inbox.remove(existingInboxHint.id);
|
|
2755
2821
|
const hint = createUserMessage({
|
|
2756
2822
|
content: [{ type: 'text', text: this.notificationText(notifications, member.memberId) }],
|
|
2757
|
-
source: { kind:
|
|
2823
|
+
source: { kind: AGENT_TEAM_PLUGIN_ID, form: 'notice', summary: INBOX_NOTICE_SUMMARY },
|
|
2758
2824
|
});
|
|
2759
2825
|
this.notifiedInbox.set(member.memberId, signature);
|
|
2760
2826
|
try {
|
|
@@ -2775,12 +2841,12 @@ let AgentTeam = (() => {
|
|
|
2775
2841
|
}
|
|
2776
2842
|
isInboxNotice(message) {
|
|
2777
2843
|
const source = message.source;
|
|
2778
|
-
return source
|
|
2844
|
+
return isAgentTeamSource(source)
|
|
2779
2845
|
&& source.form === 'notice' && source.summary === INBOX_NOTICE_SUMMARY;
|
|
2780
2846
|
}
|
|
2781
2847
|
isRecoveryNotice(message) {
|
|
2782
2848
|
const source = message.source;
|
|
2783
|
-
return source
|
|
2849
|
+
return isAgentTeamSource(source)
|
|
2784
2850
|
&& source.form === 'notice' && source.summary === RECOVERY_NOTICE_SUMMARY;
|
|
2785
2851
|
}
|
|
2786
2852
|
notificationText(notifications, readerId) {
|