gestalt-mobile 0.32.1 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/assets/index-CPQBJDsg.css +1 -0
- package/dist/client/assets/index-nivA6-l4.js +38 -0
- package/dist/client/index.html +2 -2
- package/dist/server/server/cli.js +28 -1
- package/dist/server/server/composition.js +1 -0
- package/dist/server/server/features/agent-activity/activity-dto.js +4 -1
- package/dist/server/server/features/agent-activity/model.js +25 -2
- package/dist/server/server/features/autopilot/application/policy.js +2 -2
- package/dist/server/server/features/autopilot/application/service.js +32 -13
- package/dist/server/server/features/autopilot/domain/supervised-lifecycle.js +20 -1
- package/dist/server/server/features/catalog/get-bootstrap/use-case.js +1 -0
- package/dist/server/server/features/org-plan-checkpoint/application/validate.js +8 -0
- package/dist/server/server/platform/codex/activity-facts.js +23 -0
- package/dist/server/shared/contracts/component-version.js +6 -0
- package/dist/server/shared/contracts/org-plan-checkpoint.js +77 -10
- package/gestalt-supervision-capabilities.json +1 -0
- package/package.json +1 -1
- package/dist/client/assets/index-CcWoGWyP.css +0 -1
- package/dist/client/assets/index-CxAG4Fmv.js +0 -37
package/dist/client/index.html
CHANGED
|
@@ -16,8 +16,8 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
16
16
|
<link rel="icon" href="/icons/gestalt-mobile-192.png" />
|
|
17
17
|
<link rel="apple-touch-icon" href="/icons/gestalt-mobile-180.png" />
|
|
18
18
|
<title>Gestalt Mobile</title>
|
|
19
|
-
<script type="module" crossorigin src="/assets/index-
|
|
20
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
19
|
+
<script type="module" crossorigin src="/assets/index-nivA6-l4.js"></script>
|
|
20
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CPQBJDsg.css">
|
|
21
21
|
</head>
|
|
22
22
|
<body>
|
|
23
23
|
<div id="app"></div>
|
|
@@ -28,6 +28,30 @@ Options:
|
|
|
28
28
|
--skills list List saved global skill profiles without starting the server
|
|
29
29
|
--help Show this help
|
|
30
30
|
--version Show the installed version`;
|
|
31
|
+
function managedVersion(value) {
|
|
32
|
+
return value && /^[A-Za-z0-9][A-Za-z0-9.+_-]{0,99}$/.test(value) ? value : null;
|
|
33
|
+
}
|
|
34
|
+
export function componentVersions(mobileVersion, codexVersion, environment = process.env) {
|
|
35
|
+
return [
|
|
36
|
+
{
|
|
37
|
+
id: 'gestalt',
|
|
38
|
+
label: 'Gestalt manager',
|
|
39
|
+
version: managedVersion(environment.GESTALT_MANAGER_VERSION),
|
|
40
|
+
},
|
|
41
|
+
{ id: 'gestalt-mobile', label: 'Gestalt Mobile', version: mobileVersion },
|
|
42
|
+
{
|
|
43
|
+
id: 'gestalt-agents',
|
|
44
|
+
label: 'Gestalt Agents',
|
|
45
|
+
version: managedVersion(environment.GESTALT_AGENTS_VERSION),
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: 'context-mode',
|
|
49
|
+
label: 'Context Mode',
|
|
50
|
+
version: managedVersion(environment.GESTALT_CONTEXT_MODE_VERSION),
|
|
51
|
+
},
|
|
52
|
+
{ id: 'codex', label: 'Codex CLI', version: codexVersion },
|
|
53
|
+
];
|
|
54
|
+
}
|
|
31
55
|
export async function runStartupDoctor(cwd) {
|
|
32
56
|
if (process.env.GESTALT_STARTUP_DIAGNOSTICS_DONE === '1')
|
|
33
57
|
return { ok: true, report: '' };
|
|
@@ -180,6 +204,8 @@ export async function runCli(dependencies = {}) {
|
|
|
180
204
|
stderr.write(diagnosis.report);
|
|
181
205
|
if (!diagnosis.ok)
|
|
182
206
|
stderr.write('WARNING: Startup diagnostics reported a problem; Gestalt Mobile will continue so it can show recovery controls.\n');
|
|
207
|
+
const installedCodexVersion = await (dependencies.probeCodexVersion ?? probeCodexVersion)();
|
|
208
|
+
const mobileVersion = await packageVersion(moduleUrl);
|
|
183
209
|
const app = await (dependencies.compose ?? composeRelayApp)({
|
|
184
210
|
root: config.root,
|
|
185
211
|
dataDir: config.dataDir,
|
|
@@ -187,7 +213,8 @@ export async function runCli(dependencies = {}) {
|
|
|
187
213
|
passkeyAuthEnabled: config.passkeyAuthEnabled,
|
|
188
214
|
staticDir: packagedClientDir(moduleUrl),
|
|
189
215
|
profiles: new LauncherProfileCatalog(),
|
|
190
|
-
installedCodexVersion
|
|
216
|
+
installedCodexVersion,
|
|
217
|
+
componentVersions: componentVersions(mobileVersion, installedCodexVersion, dependencies.environment),
|
|
191
218
|
startAppServers: true,
|
|
192
219
|
homeDirectory: dependencies.homeDirectory,
|
|
193
220
|
explicitSkillProfile,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Designed by Denis Roio <jaromil@dyne.org>
|
|
4
4
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
5
|
*/
|
|
6
|
-
/** Public roster projection:
|
|
6
|
+
/** Public roster projection: expose bounded context use, never raw host metrics or identifiers. */
|
|
7
7
|
export function toAgentActivityDto(snapshot) {
|
|
8
8
|
return {
|
|
9
9
|
sessionId: snapshot.sessionId,
|
|
@@ -19,6 +19,9 @@ export function toAgentActivityDto(snapshot) {
|
|
|
19
19
|
? { continuationGeneration: child.continuationGeneration }
|
|
20
20
|
: {}),
|
|
21
21
|
...(child.outcome ? { outcome: child.outcome } : {}),
|
|
22
|
+
...(child.contextUsedPercent !== undefined
|
|
23
|
+
? { contextUsedPercent: child.contextUsedPercent }
|
|
24
|
+
: {}),
|
|
22
25
|
ownedProcesses: (child.ownedProcesses ?? []).slice(0, 64).map((process) => ({
|
|
23
26
|
state: process.state,
|
|
24
27
|
ownership: process.ownership,
|
|
@@ -40,6 +40,12 @@ export function projectAgentActivity(current, fact) {
|
|
|
40
40
|
const child = current.subagents.find((candidate) => candidate.threadId === fact.threadId);
|
|
41
41
|
if (!child)
|
|
42
42
|
return current;
|
|
43
|
+
if (fact.kind === 'contextUsage' && validPercent(fact.contextUsedPercent)) {
|
|
44
|
+
const subagents = Object.freeze(current.subagents.map((candidate) => candidate.id === child.id
|
|
45
|
+
? Object.freeze({ ...candidate, contextUsedPercent: fact.contextUsedPercent })
|
|
46
|
+
: candidate));
|
|
47
|
+
return Object.freeze({ ...current, subagents });
|
|
48
|
+
}
|
|
43
49
|
const childFact = { ...fact };
|
|
44
50
|
delete childFact.threadId;
|
|
45
51
|
return projectAgentActivity(current, {
|
|
@@ -53,7 +59,9 @@ export function projectAgentActivity(current, fact) {
|
|
|
53
59
|
const root = {
|
|
54
60
|
...current.root,
|
|
55
61
|
observedAt,
|
|
56
|
-
lastActivityAt:
|
|
62
|
+
lastActivityAt: fact.kind === 'contextUsage'
|
|
63
|
+
? current.root.lastActivityAt
|
|
64
|
+
: later(current.root.lastActivityAt, fact.occurredAt),
|
|
57
65
|
};
|
|
58
66
|
const children = new Map(current.subagents.map((child) => [child.id, child]));
|
|
59
67
|
if (fact.kind === 'turnStarted')
|
|
@@ -75,6 +83,8 @@ export function projectAgentActivity(current, fact) {
|
|
|
75
83
|
Object.assign(root, { state: 'disconnected', reason: 'processExited' });
|
|
76
84
|
if (fact.kind === 'threadStatus')
|
|
77
85
|
applyStatus(root, fact.status);
|
|
86
|
+
if (fact.kind === 'contextUsage' && validPercent(fact.contextUsedPercent))
|
|
87
|
+
root.contextUsedPercent = fact.contextUsedPercent;
|
|
78
88
|
if (fact.kind === 'collaboration') {
|
|
79
89
|
// Older servers may omit experimental child metadata. That is a capability
|
|
80
90
|
// downgrade, not evidence that the root process disappeared.
|
|
@@ -117,6 +127,11 @@ export function projectAgentActivity(current, fact) {
|
|
|
117
127
|
? { canonicalTaskName: 'final_review' }
|
|
118
128
|
: {}),
|
|
119
129
|
...(outcome ? { outcome } : {}),
|
|
130
|
+
...(validPercent(fact.contextUsedPercent)
|
|
131
|
+
? { contextUsedPercent: fact.contextUsedPercent }
|
|
132
|
+
: validPercent(before?.contextUsedPercent)
|
|
133
|
+
? { contextUsedPercent: before.contextUsedPercent }
|
|
134
|
+
: {}),
|
|
120
135
|
...(fact.childOwnedProcesses
|
|
121
136
|
? { ownedProcesses: Object.freeze([...fact.childOwnedProcesses]) }
|
|
122
137
|
: before?.ownedProcesses
|
|
@@ -224,6 +239,9 @@ function aggregate(children) {
|
|
|
224
239
|
function validTimestamp(value) {
|
|
225
240
|
return !Number.isNaN(Date.parse(value));
|
|
226
241
|
}
|
|
242
|
+
function validPercent(value) {
|
|
243
|
+
return typeof value === 'number' && Number.isInteger(value) && value >= 0 && value <= 100;
|
|
244
|
+
}
|
|
227
245
|
function later(left, right) {
|
|
228
246
|
return Date.parse(right) > Date.parse(left) ? right : left;
|
|
229
247
|
}
|
|
@@ -235,12 +253,17 @@ function semantic(snapshot) {
|
|
|
235
253
|
sessionId: snapshot.sessionId,
|
|
236
254
|
rootThreadId: snapshot.rootThreadId,
|
|
237
255
|
confidence: snapshot.confidence,
|
|
238
|
-
root: {
|
|
256
|
+
root: {
|
|
257
|
+
state: snapshot.root.state,
|
|
258
|
+
reason: snapshot.root.reason,
|
|
259
|
+
contextUsedPercent: snapshot.root.contextUsedPercent,
|
|
260
|
+
},
|
|
239
261
|
subagents: snapshot.subagents.map((child) => ({
|
|
240
262
|
id: child.id,
|
|
241
263
|
threadId: child.threadId,
|
|
242
264
|
nickname: child.nickname,
|
|
243
265
|
role: child.role,
|
|
266
|
+
contextUsedPercent: child.contextUsedPercent,
|
|
244
267
|
model: child.model,
|
|
245
268
|
taskPath: child.taskPath,
|
|
246
269
|
canonicalTaskName: child.canonicalTaskName,
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Designed by Denis Roio <jaromil@dyne.org>
|
|
4
4
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
5
|
*/
|
|
6
|
-
export const AUTOPILOT_PROMPT_VERSION = '
|
|
7
|
-
export const AUTOPILOT_CONTINUATION_PROMPT = 'Inspect the active supervised Org Plan. Refer to every L1 as L<a> and each nested L2 as L<a>.<b>, using one-based positions. For a subagent dedicated to that position, use the collaboration-safe task_name l<a> or l<a>_<b> and refer to it by its canonical L label. If the active L1 is still WIP and its executor stopped or is idle after a partial report or L2 checkpoint, call followup_task on that same executor before sending any response. Treat a status question as an interruption: answer briefly, then perform that continuation in the same turn.
|
|
6
|
+
export const AUTOPILOT_PROMPT_VERSION = 'v7';
|
|
7
|
+
export const AUTOPILOT_CONTINUATION_PROMPT = 'Inspect the active supervised Org Plan. Refer to every L1 as L<a> and each nested L2 as L<a>.<b>, using one-based positions. For a subagent dedicated to that position, use the collaboration-safe task_name l<a> or l<a>_<b> and refer to it by its canonical L label. If the active L1 is still WIP and its executor stopped or is idle after a partial report or L2 checkpoint, call followup_task on that same executor before sending any response. Treat a status question as an interruption: answer briefly, then perform that continuation in the same turn. Before sending any blocker response or yielding because progress cannot continue safely, call gestalt_org_plan_attention for the matching decision-table blocker; never rely on blocker prose as the signal. Do not call it for routine progress or a recoverable failure. When the Autopilot probe is active, register gestalt_autopilot_wait_lease with an observable wake condition, declare genuine attention, or immediately do actionable work; never acknowledge waiting in prose. Do not send a status-only response.';
|
|
8
8
|
export const AUTOPILOT_EXECUTOR_CONTINUATION_PROMPT = 'Continue the same assigned Org L1 from its durable state. A prior turn ending did not complete the objective. Consume any supplied process result, take the next legal L2 action, and report only at the L1 review boundary or through structured attention.';
|
|
9
9
|
/** Builds a physical launch instruction while keeping durable L1 identity canonical. */
|
|
10
10
|
export function autopilotExecutorLaunchPrompt(identity) {
|
|
@@ -462,16 +462,20 @@ export class AutopilotCoordinator {
|
|
|
462
462
|
validStructuredBlock(state.blocking);
|
|
463
463
|
if (checkpointBoundary && state?.checkpoints) {
|
|
464
464
|
const occurredAt = this.deps.now();
|
|
465
|
+
const pendingKind = state.checkpoints.pendingKind ??
|
|
466
|
+
(state.checkpoints.terminalReviewAccepted ? 'terminalReviewAccepted' : 'l1Accepted');
|
|
465
467
|
this.persist({
|
|
466
468
|
...state,
|
|
467
|
-
checkpoints: { ...state.checkpoints, pendingTurnId: null },
|
|
469
|
+
checkpoints: { ...state.checkpoints, pendingTurnId: null, pendingKind: null },
|
|
468
470
|
updatedAt: occurredAt,
|
|
469
471
|
}, undefined, [
|
|
470
472
|
{
|
|
471
473
|
sessionId,
|
|
472
|
-
type:
|
|
473
|
-
? 'org-plan.
|
|
474
|
-
: '
|
|
474
|
+
type: pendingKind === 'l2Completed'
|
|
475
|
+
? 'org-plan.step-reported'
|
|
476
|
+
: pendingKind === 'terminalReviewAccepted'
|
|
477
|
+
? 'org-plan.terminal-review-reported'
|
|
478
|
+
: 'org-plan.milestone-reported',
|
|
475
479
|
payload: { turnId: state.checkpoints.pendingTurnId },
|
|
476
480
|
occurredAt,
|
|
477
481
|
},
|
|
@@ -503,32 +507,47 @@ export class AutopilotCoordinator {
|
|
|
503
507
|
const previous = prior.checkpoints;
|
|
504
508
|
if (previous && previous.planIdentity !== retained.identity)
|
|
505
509
|
return false;
|
|
506
|
-
const
|
|
510
|
+
const reportedL1Ids = previous?.reportedL1Ids ?? [];
|
|
511
|
+
const reportedL2Ids = previous?.reportedL2Ids ?? [];
|
|
507
512
|
const key = createHash('sha256').update(JSON.stringify(checkpoint)).digest('hex');
|
|
508
513
|
const acceptedKeys = previous?.acceptedKeys ?? [];
|
|
509
514
|
if (acceptedKeys.includes(key))
|
|
510
515
|
return true;
|
|
511
|
-
|
|
516
|
+
const l2Key = checkpoint.kind === 'l2Completed' ? JSON.stringify([checkpoint.l1Id, checkpoint.l2Id]) : null;
|
|
517
|
+
if (l2Key && reportedL2Ids.includes(l2Key))
|
|
518
|
+
return false;
|
|
519
|
+
if (checkpoint.kind === 'l1Accepted' && reportedL1Ids.includes(checkpoint.l1Id))
|
|
512
520
|
return false;
|
|
513
521
|
if (checkpoint.kind === 'terminalReviewAccepted' && previous?.terminalReviewAccepted)
|
|
514
522
|
return false;
|
|
515
523
|
const checkpoints = {
|
|
516
524
|
protocolVersion: 1,
|
|
517
525
|
planIdentity: retained.identity,
|
|
518
|
-
|
|
526
|
+
reportedL2Ids: l2Key ? [...reportedL2Ids, l2Key] : reportedL2Ids,
|
|
527
|
+
reportedL1Ids: checkpoint.kind === 'l1Accepted' ? [...reportedL1Ids, checkpoint.l1Id] : reportedL1Ids,
|
|
519
528
|
acceptedKeys: [...acceptedKeys, key],
|
|
520
529
|
pendingTurnId: turnId,
|
|
530
|
+
pendingKind: checkpoint.kind,
|
|
521
531
|
terminalReviewAccepted: checkpoint.kind === 'terminalReviewAccepted' || previous?.terminalReviewAccepted === true,
|
|
522
532
|
};
|
|
523
533
|
this.persist({ ...prior, checkpoints, updatedAt: occurredAt }, undefined, [
|
|
524
534
|
{
|
|
525
535
|
sessionId,
|
|
526
|
-
type: checkpoint.kind === '
|
|
527
|
-
? 'org-plan.
|
|
528
|
-
:
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
536
|
+
type: checkpoint.kind === 'l2Completed'
|
|
537
|
+
? 'org-plan.step-checkpointed'
|
|
538
|
+
: checkpoint.kind === 'l1Accepted'
|
|
539
|
+
? 'org-plan.milestone-checkpointed'
|
|
540
|
+
: 'org-plan.terminal-review-checkpointed',
|
|
541
|
+
payload: checkpoint.kind === 'l2Completed'
|
|
542
|
+
? {
|
|
543
|
+
l1Id: checkpoint.l1Id,
|
|
544
|
+
l2Id: checkpoint.l2Id,
|
|
545
|
+
position: checkpoint.position,
|
|
546
|
+
turnId,
|
|
547
|
+
}
|
|
548
|
+
: checkpoint.kind === 'l1Accepted'
|
|
549
|
+
? { l1Id: checkpoint.l1Id, position: checkpoint.position, turnId }
|
|
550
|
+
: { turnId },
|
|
532
551
|
occurredAt,
|
|
533
552
|
},
|
|
534
553
|
]);
|
|
@@ -152,6 +152,13 @@ function parseCheckpoints(value) {
|
|
|
152
152
|
const planIdentity = boundedText(value.planIdentity);
|
|
153
153
|
if (value.protocolVersion !== 1 || !planIdentity)
|
|
154
154
|
return undefined;
|
|
155
|
+
const rawReportedL2Ids = value.reportedL2Ids ?? [];
|
|
156
|
+
if (!Array.isArray(rawReportedL2Ids) || rawReportedL2Ids.length > 512)
|
|
157
|
+
return undefined;
|
|
158
|
+
const reportedL2Ids = rawReportedL2Ids.map((id) => boundedText(id)).filter(Boolean);
|
|
159
|
+
if (reportedL2Ids.length !== rawReportedL2Ids.length ||
|
|
160
|
+
new Set(reportedL2Ids).size !== reportedL2Ids.length)
|
|
161
|
+
return undefined;
|
|
155
162
|
if (!Array.isArray(value.reportedL1Ids) || value.reportedL1Ids.length > 128)
|
|
156
163
|
return undefined;
|
|
157
164
|
const reportedL1Ids = value.reportedL1Ids
|
|
@@ -160,7 +167,7 @@ function parseCheckpoints(value) {
|
|
|
160
167
|
if (reportedL1Ids.length !== value.reportedL1Ids.length ||
|
|
161
168
|
new Set(reportedL1Ids).size !== reportedL1Ids.length)
|
|
162
169
|
return undefined;
|
|
163
|
-
if (!Array.isArray(value.acceptedKeys) || value.acceptedKeys.length >
|
|
170
|
+
if (!Array.isArray(value.acceptedKeys) || value.acceptedKeys.length > 768)
|
|
164
171
|
return undefined;
|
|
165
172
|
const acceptedKeys = value.acceptedKeys
|
|
166
173
|
.map((key) => boundedText(key))
|
|
@@ -173,12 +180,24 @@ function parseCheckpoints(value) {
|
|
|
173
180
|
return undefined;
|
|
174
181
|
if (typeof value.terminalReviewAccepted !== 'boolean')
|
|
175
182
|
return undefined;
|
|
183
|
+
const pendingKind = value.pendingKind === undefined
|
|
184
|
+
? pendingTurnId
|
|
185
|
+
? value.terminalReviewAccepted
|
|
186
|
+
? 'terminalReviewAccepted'
|
|
187
|
+
: 'l1Accepted'
|
|
188
|
+
: null
|
|
189
|
+
: value.pendingKind;
|
|
190
|
+
if (pendingKind !== null &&
|
|
191
|
+
!['l2Completed', 'l1Accepted', 'terminalReviewAccepted'].includes(String(pendingKind)))
|
|
192
|
+
return undefined;
|
|
176
193
|
return {
|
|
177
194
|
protocolVersion: 1,
|
|
178
195
|
planIdentity,
|
|
196
|
+
reportedL2Ids,
|
|
179
197
|
reportedL1Ids,
|
|
180
198
|
acceptedKeys,
|
|
181
199
|
pendingTurnId,
|
|
200
|
+
pendingKind: pendingKind,
|
|
182
201
|
terminalReviewAccepted: value.terminalReviewAccepted,
|
|
183
202
|
};
|
|
184
203
|
}
|
|
@@ -13,6 +13,14 @@ export function validOrgPlanCheckpoint(input) {
|
|
|
13
13
|
if (!l1)
|
|
14
14
|
return false;
|
|
15
15
|
const position = `L${plan.steps.indexOf(l1) + 1}`;
|
|
16
|
+
if (checkpoint.kind === 'l2Completed') {
|
|
17
|
+
const l2 = l1.children.find((step) => step.id === checkpoint.l2Id);
|
|
18
|
+
if (!l2)
|
|
19
|
+
return false;
|
|
20
|
+
return (checkpoint.position === `${position}.${l1.children.indexOf(l2) + 1}` &&
|
|
21
|
+
l2.state === 'DONE' &&
|
|
22
|
+
l1.reviewStatus === 'UNREVIEWED');
|
|
23
|
+
}
|
|
16
24
|
return (checkpoint.position === position &&
|
|
17
25
|
l1.state === 'DONE' &&
|
|
18
26
|
l1.reviewStatus === 'REVIEWED' &&
|
|
@@ -68,6 +68,29 @@ export function decodeAgentActivityFacts(sessionId, occurredAt, input) {
|
|
|
68
68
|
},
|
|
69
69
|
]
|
|
70
70
|
: [];
|
|
71
|
+
if (input.method === 'thread/tokenUsage/updated') {
|
|
72
|
+
const usage = record(params.tokenUsage);
|
|
73
|
+
const last = record(usage?.last);
|
|
74
|
+
const totalTokens = last?.totalTokens;
|
|
75
|
+
const modelContextWindow = usage?.modelContextWindow;
|
|
76
|
+
if (!id(params.threadId) ||
|
|
77
|
+
typeof totalTokens !== 'number' ||
|
|
78
|
+
!Number.isFinite(totalTokens) ||
|
|
79
|
+
totalTokens < 0 ||
|
|
80
|
+
typeof modelContextWindow !== 'number' ||
|
|
81
|
+
!Number.isFinite(modelContextWindow) ||
|
|
82
|
+
modelContextWindow <= 0)
|
|
83
|
+
return [];
|
|
84
|
+
return [
|
|
85
|
+
{
|
|
86
|
+
sessionId,
|
|
87
|
+
occurredAt,
|
|
88
|
+
kind: 'contextUsage',
|
|
89
|
+
threadId: id(params.threadId),
|
|
90
|
+
contextUsedPercent: Math.min(100, Math.max(0, Math.round((totalTokens / modelContextWindow) * 100))),
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
}
|
|
71
94
|
if (input.method === 'item/started' || input.method === 'item/completed') {
|
|
72
95
|
const item = record(params.item);
|
|
73
96
|
if (item?.type !== 'collabAgentToolCall' && item?.type !== 'collabToolCall')
|
|
@@ -10,17 +10,19 @@ const record = (value) => typeof value === 'object' && value !== null && !Array.
|
|
|
10
10
|
export const gestaltOrgPlanCheckpointDynamicTool = {
|
|
11
11
|
type: 'function',
|
|
12
12
|
name: GESTALT_ORG_PLAN_CHECKPOINT_TOOL_NAME,
|
|
13
|
-
description: 'Record a validated supervised Org Plan report boundary. This is not review authority and must only be called by the active root after the Org Plan helper has recorded the matching
|
|
13
|
+
description: 'Record a validated supervised Org Plan report boundary. This is not review authority and must only be called by the active root after the Org Plan helper has recorded the matching state.',
|
|
14
14
|
inputSchema: {
|
|
15
15
|
type: 'object',
|
|
16
16
|
additionalProperties: false,
|
|
17
|
-
required: ['version', 'kind', 'planIdentity'
|
|
17
|
+
required: ['version', 'kind', 'planIdentity'],
|
|
18
18
|
properties: {
|
|
19
19
|
version: { const: 1 },
|
|
20
|
-
kind: { type: 'string', enum: ['l1Accepted', 'terminalReviewAccepted'] },
|
|
20
|
+
kind: { type: 'string', enum: ['l2Completed', 'l1Accepted', 'terminalReviewAccepted'] },
|
|
21
21
|
planIdentity: { type: 'string', minLength: 1, maxLength: 128 },
|
|
22
22
|
l1Id: { type: 'string', minLength: 1, maxLength: 128 },
|
|
23
|
-
|
|
23
|
+
l2Id: { type: 'string', minLength: 1, maxLength: 128 },
|
|
24
|
+
position: { type: 'string', pattern: '^L[1-9][0-9]*(\\.[1-9][0-9]*)?$', maxLength: 32 },
|
|
25
|
+
status: { const: 'DONE' },
|
|
24
26
|
verdict: { const: 'ACCEPT' },
|
|
25
27
|
commit: {
|
|
26
28
|
oneOf: [
|
|
@@ -43,24 +45,86 @@ export const gestaltOrgPlanCheckpointDynamicTool = {
|
|
|
43
45
|
],
|
|
44
46
|
},
|
|
45
47
|
findings: { type: 'string', minLength: 1, maxLength: 600 },
|
|
48
|
+
changes: { type: 'string', minLength: 1, maxLength: 600 },
|
|
49
|
+
files: { type: 'string', minLength: 1, maxLength: 600 },
|
|
46
50
|
tests: { type: 'string', minLength: 1, maxLength: 600 },
|
|
47
51
|
},
|
|
48
52
|
oneOf: [
|
|
49
|
-
{
|
|
53
|
+
{
|
|
54
|
+
properties: { kind: { const: 'l2Completed' } },
|
|
55
|
+
required: ['l1Id', 'l2Id', 'position', 'status', 'changes', 'files', 'tests'],
|
|
56
|
+
not: {
|
|
57
|
+
anyOf: [{ required: ['verdict'] }, { required: ['commit'] }, { required: ['findings'] }],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
properties: { kind: { const: 'l1Accepted' } },
|
|
62
|
+
required: ['l1Id', 'position', 'verdict', 'commit'],
|
|
63
|
+
not: {
|
|
64
|
+
anyOf: [
|
|
65
|
+
{ required: ['l2Id'] },
|
|
66
|
+
{ required: ['status'] },
|
|
67
|
+
{ required: ['changes'] },
|
|
68
|
+
{ required: ['files'] },
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
},
|
|
50
72
|
{
|
|
51
73
|
properties: { kind: { const: 'terminalReviewAccepted' } },
|
|
74
|
+
required: ['verdict'],
|
|
52
75
|
not: {
|
|
53
|
-
anyOf: [
|
|
76
|
+
anyOf: [
|
|
77
|
+
{ required: ['l1Id'] },
|
|
78
|
+
{ required: ['l2Id'] },
|
|
79
|
+
{ required: ['position'] },
|
|
80
|
+
{ required: ['status'] },
|
|
81
|
+
{ required: ['commit'] },
|
|
82
|
+
{ required: ['changes'] },
|
|
83
|
+
{ required: ['files'] },
|
|
84
|
+
],
|
|
54
85
|
},
|
|
55
86
|
},
|
|
56
87
|
],
|
|
57
88
|
},
|
|
58
89
|
};
|
|
59
90
|
export function parseOrgPlanCheckpoint(value) {
|
|
60
|
-
if (!record(value) ||
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
value.
|
|
91
|
+
if (!record(value) || value.version !== 1 || !bounded(value.planIdentity, 128))
|
|
92
|
+
return null;
|
|
93
|
+
if (value.kind === 'l2Completed') {
|
|
94
|
+
if (!bounded(value.l1Id, 128) ||
|
|
95
|
+
!bounded(value.l2Id, 128) ||
|
|
96
|
+
!isL2Position(value.position) ||
|
|
97
|
+
value.status !== 'DONE' ||
|
|
98
|
+
!bounded(value.changes, 600) ||
|
|
99
|
+
!bounded(value.files, 600) ||
|
|
100
|
+
!bounded(value.tests, 600) ||
|
|
101
|
+
Object.keys(value).some((key) => ![
|
|
102
|
+
'version',
|
|
103
|
+
'kind',
|
|
104
|
+
'planIdentity',
|
|
105
|
+
'l1Id',
|
|
106
|
+
'l2Id',
|
|
107
|
+
'position',
|
|
108
|
+
'status',
|
|
109
|
+
'changes',
|
|
110
|
+
'files',
|
|
111
|
+
'tests',
|
|
112
|
+
].includes(key)))
|
|
113
|
+
return null;
|
|
114
|
+
return {
|
|
115
|
+
version: 1,
|
|
116
|
+
kind: 'l2Completed',
|
|
117
|
+
planIdentity: value.planIdentity,
|
|
118
|
+
l1Id: value.l1Id,
|
|
119
|
+
l2Id: value.l2Id,
|
|
120
|
+
position: value.position,
|
|
121
|
+
status: 'DONE',
|
|
122
|
+
changes: value.changes,
|
|
123
|
+
files: value.files,
|
|
124
|
+
tests: value.tests,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
if (value.verdict !== 'ACCEPT')
|
|
64
128
|
return null;
|
|
65
129
|
if (value.findings !== undefined && !bounded(value.findings, 600))
|
|
66
130
|
return null;
|
|
@@ -118,6 +182,9 @@ export function toOrgPlanCheckpointToolResponse() {
|
|
|
118
182
|
function isPosition(value) {
|
|
119
183
|
return typeof value === 'string' && /^L[1-9][0-9]*$/.test(value);
|
|
120
184
|
}
|
|
185
|
+
function isL2Position(value) {
|
|
186
|
+
return typeof value === 'string' && /^L[1-9][0-9]*\.[1-9][0-9]*$/.test(value);
|
|
187
|
+
}
|
|
121
188
|
function parseCommit(value) {
|
|
122
189
|
if (value.kind === 'notRequired' && Object.keys(value).length === 1)
|
|
123
190
|
return { kind: 'notRequired' };
|