gestalt-mobile 0.36.0 → 0.36.1
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/server/server/composition.contracts.js +35 -4
- package/dist/server/server/composition.js +17 -3
- package/dist/server/server/features/org-plan-checkpoint/application/validate.js +40 -0
- package/dist/server/server/platform/codex/server-request.js +14 -0
- package/dist/server/server/platform/codex/session-runtime.js +5 -1
- package/dist/server/shared/contracts/org-plan-attention.js +15 -1
- package/package.json +1 -1
|
@@ -196,7 +196,7 @@ async function createProductionAutopilotFixture(overrides = {}) {
|
|
|
196
196
|
await installAutopilotPlan(app, sessionId, workspacePath, 'autopilot');
|
|
197
197
|
return { app, dataDir, handles, root, sessionId, workspacePath };
|
|
198
198
|
}
|
|
199
|
-
async function holdCheckpointRequest(fixture, timers, requestId, expectHeld = true) {
|
|
199
|
+
async function holdCheckpointRequest(fixture, timers, requestId, expectHeld = true, compact = false) {
|
|
200
200
|
timers.find((timer) => !timer.cancelled).callback();
|
|
201
201
|
await vi.waitFor(() => expect(fixture.handles.flatMap((candidate) => candidate.requests.filter((request) => request.method === 'turn/start'))).toHaveLength(1));
|
|
202
202
|
const handle = fixture.handles.find((candidate) => candidate.requests.some((request) => request.method === 'turn/start'));
|
|
@@ -218,7 +218,18 @@ async function holdCheckpointRequest(fixture, timers, requestId, expectHeld = tr
|
|
|
218
218
|
const startsBefore = fixture.handles
|
|
219
219
|
.flatMap((candidate) => candidate.requests)
|
|
220
220
|
.filter((request) => request.method === 'turn/start').length;
|
|
221
|
-
const response = handle.request(
|
|
221
|
+
const response = handle.request(compact
|
|
222
|
+
? {
|
|
223
|
+
id: requestId,
|
|
224
|
+
method: 'item/tool/call',
|
|
225
|
+
params: {
|
|
226
|
+
threadId,
|
|
227
|
+
turnId,
|
|
228
|
+
tool: 'gestalt_org_plan_checkpoint',
|
|
229
|
+
arguments: { kind: 'l2Completed' },
|
|
230
|
+
},
|
|
231
|
+
}
|
|
232
|
+
: l2CheckpointCall(requestId, threadId, turnId, planIdentity));
|
|
222
233
|
// The caller can be synchronously rejected by a coordinator exception.
|
|
223
234
|
// Mark it observed immediately; callers still assert its exact outcome.
|
|
224
235
|
void response.catch(() => { });
|
|
@@ -915,6 +926,28 @@ describe('production composition', () => {
|
|
|
915
926
|
.filter((request) => request.method === 'turn/start')).toHaveLength(2));
|
|
916
927
|
await fixture.app.close();
|
|
917
928
|
});
|
|
929
|
+
it('expands and acknowledges a compact checkpoint signal from the active root', async () => {
|
|
930
|
+
const timers = [];
|
|
931
|
+
const fixture = await createProductionAutopilotFixture({
|
|
932
|
+
autopilotSchedule: (callback) => {
|
|
933
|
+
const timer = { callback, cancelled: false };
|
|
934
|
+
timers.push(timer);
|
|
935
|
+
return () => {
|
|
936
|
+
timer.cancelled = true;
|
|
937
|
+
};
|
|
938
|
+
},
|
|
939
|
+
});
|
|
940
|
+
const { handle, response } = await holdCheckpointRequest(fixture, timers, 812, true, true);
|
|
941
|
+
handle.responseSettled({ id: 812, outcome: 'resultWritten' });
|
|
942
|
+
await expect(response).resolves.toEqual(toOrgPlanCheckpointToolResponse());
|
|
943
|
+
const database = new DatabaseSync(join(fixture.dataDir, 'relay.sqlite'));
|
|
944
|
+
const checkpoint = database
|
|
945
|
+
.prepare("SELECT count(*) AS count FROM session_events WHERE session_id = ? AND type = 'org-plan.step-checkpointed'")
|
|
946
|
+
.get(fixture.sessionId);
|
|
947
|
+
database.close();
|
|
948
|
+
expect(checkpoint.count).toBe(1);
|
|
949
|
+
await fixture.app.close();
|
|
950
|
+
});
|
|
918
951
|
it('leaves an unsupported checkpoint exception before persistence inert', async () => {
|
|
919
952
|
let coordinator;
|
|
920
953
|
const timers = [];
|
|
@@ -3652,8 +3685,6 @@ describe('production composition', () => {
|
|
|
3652
3685
|
tool: 'gestalt_org_plan_attention',
|
|
3653
3686
|
arguments: {
|
|
3654
3687
|
reason: 'permissionRequired',
|
|
3655
|
-
summary: 'A protected release needs approval.',
|
|
3656
|
-
requestedAction: 'Grant the release permission.',
|
|
3657
3688
|
resumeCondition: 'permissionGranted',
|
|
3658
3689
|
},
|
|
3659
3690
|
},
|
|
@@ -46,9 +46,9 @@ import { RelaySession, } from './features/sessions/model/relay-session.js';
|
|
|
46
46
|
import { AgentActivityRegistry } from './features/agent-activity/registry.js';
|
|
47
47
|
import { toAgentActivityDto } from './features/agent-activity/activity-dto.js';
|
|
48
48
|
import { decodeAgentActivityFacts } from './platform/codex/activity-facts.js';
|
|
49
|
-
import { completedCommandId, isAgentCapacityRecoveryCall, isAutopilotWaitLeaseCall, resolvedServerRequestId, toPendingInteraction, } from './platform/codex/server-request.js';
|
|
49
|
+
import { completedCommandId, compactOrgPlanCheckpointKind, isAgentCapacityRecoveryCall, isAutopilotWaitLeaseCall, resolvedServerRequestId, toPendingInteraction, } from './platform/codex/server-request.js';
|
|
50
50
|
import { parseOrgPlanCheckpoint, toOrgPlanCheckpointToolResponse, } from '../shared/contracts/org-plan-checkpoint.js';
|
|
51
|
-
import { validOrgPlanCheckpoint } from './features/org-plan-checkpoint/application/validate.js';
|
|
51
|
+
import { resolveOrgPlanCheckpointSignal, validOrgPlanCheckpoint, } from './features/org-plan-checkpoint/application/validate.js';
|
|
52
52
|
import { isValidInteractionResponse, isValidQuizInteractionResponse, } from './features/sessions/interaction/response-validator.js';
|
|
53
53
|
import { promoteRecentThread } from './features/sessions/promote-recent-thread/use-case.js';
|
|
54
54
|
import { FilesystemSkillProfileStore } from './platform/skills/filesystem-skill-profile-store.js';
|
|
@@ -676,10 +676,24 @@ export async function composeRelayApp(options) {
|
|
|
676
676
|
if (completedSession)
|
|
677
677
|
events.publish(journal.append(sessionId, 'session.updated', completedSession, occurredAt));
|
|
678
678
|
}, (sessionId, request, origin) => {
|
|
679
|
-
|
|
679
|
+
let rawInteraction = toPendingInteraction(request);
|
|
680
680
|
const session = withPendingInteractions(sessions.find(sessionId));
|
|
681
681
|
if (!session)
|
|
682
682
|
return false;
|
|
683
|
+
const compactCheckpointKind = compactOrgPlanCheckpointKind(request);
|
|
684
|
+
if (!rawInteraction && compactCheckpointKind) {
|
|
685
|
+
const retained = supervisedPlans.find(sessionId);
|
|
686
|
+
const identity = supervisedPlans.identity(sessionId);
|
|
687
|
+
const checkpoint = retained && identity
|
|
688
|
+
? resolveOrgPlanCheckpointSignal(compactCheckpointKind, retained, identity)
|
|
689
|
+
: null;
|
|
690
|
+
if (checkpoint)
|
|
691
|
+
rawInteraction = {
|
|
692
|
+
requestId: String(request.id),
|
|
693
|
+
kind: 'orgPlanCheckpoint',
|
|
694
|
+
payload: checkpoint,
|
|
695
|
+
};
|
|
696
|
+
}
|
|
683
697
|
if (!rawInteraction && isAgentCapacityRecoveryCall(request)) {
|
|
684
698
|
const rootOwned = origin.kind === 'root' && origin.physicalTurnId === session.activeTurnId;
|
|
685
699
|
return (rootOwned &&
|
|
@@ -3,6 +3,46 @@
|
|
|
3
3
|
* Designed by Denis Roio <jaromil@dyne.org>
|
|
4
4
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
5
|
*/
|
|
6
|
+
/** Expands a host-owned compact boundary signal from authoritative plan state. */
|
|
7
|
+
export function resolveOrgPlanCheckpointSignal(kind, plan, planIdentity) {
|
|
8
|
+
if (kind === 'terminalReviewAccepted')
|
|
9
|
+
return {
|
|
10
|
+
version: 1,
|
|
11
|
+
kind,
|
|
12
|
+
planIdentity,
|
|
13
|
+
verdict: 'ACCEPT',
|
|
14
|
+
};
|
|
15
|
+
const selected = plan.steps.find((step) => step.id === plan.currentStepId) ??
|
|
16
|
+
[...plan.steps].reverse().find((step) => step.state === 'DONE' || step.state === 'WIP');
|
|
17
|
+
if (!selected)
|
|
18
|
+
return null;
|
|
19
|
+
const l1Position = `L${plan.steps.indexOf(selected) + 1}`;
|
|
20
|
+
if (kind === 'l1Accepted')
|
|
21
|
+
return {
|
|
22
|
+
version: 1,
|
|
23
|
+
kind,
|
|
24
|
+
planIdentity,
|
|
25
|
+
l1Id: selected.id,
|
|
26
|
+
position: l1Position,
|
|
27
|
+
verdict: 'ACCEPT',
|
|
28
|
+
commit: { kind: 'notRequired' },
|
|
29
|
+
};
|
|
30
|
+
const child = [...selected.children].reverse().find((step) => step.state === 'DONE');
|
|
31
|
+
if (!child)
|
|
32
|
+
return null;
|
|
33
|
+
return {
|
|
34
|
+
version: 1,
|
|
35
|
+
kind,
|
|
36
|
+
planIdentity,
|
|
37
|
+
l1Id: selected.id,
|
|
38
|
+
l2Id: child.id,
|
|
39
|
+
position: `${l1Position}.${selected.children.indexOf(child) + 1}`,
|
|
40
|
+
status: 'DONE',
|
|
41
|
+
changes: 'Validated from the authoritative Org Plan boundary.',
|
|
42
|
+
files: 'Recorded by the supervised executor.',
|
|
43
|
+
tests: 'Focused verification recorded before the boundary.',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
6
46
|
export function validOrgPlanCheckpoint(input) {
|
|
7
47
|
const { checkpoint, plan, planIdentity, rootOwned } = input;
|
|
8
48
|
if (!rootOwned || checkpoint.planIdentity !== planIdentity)
|
|
@@ -70,6 +70,20 @@ export function isAutopilotWaitLeaseCall(input) {
|
|
|
70
70
|
isRecord(input.params) &&
|
|
71
71
|
input.params.tool === GESTALT_AUTOPILOT_WAIT_LEASE_TOOL_NAME);
|
|
72
72
|
}
|
|
73
|
+
/** Compatibility signal used by newer hosts that let Mobile derive boundary details. */
|
|
74
|
+
export function compactOrgPlanCheckpointKind(input) {
|
|
75
|
+
if (!Number.isSafeInteger(input.id) ||
|
|
76
|
+
input.method !== 'item/tool/call' ||
|
|
77
|
+
!isRecord(input.params) ||
|
|
78
|
+
input.params.tool !== GESTALT_ORG_PLAN_CHECKPOINT_TOOL_NAME ||
|
|
79
|
+
!isRecord(input.params.arguments) ||
|
|
80
|
+
Object.keys(input.params.arguments).length !== 1)
|
|
81
|
+
return null;
|
|
82
|
+
const kind = input.params.arguments.kind;
|
|
83
|
+
return kind === 'l2Completed' || kind === 'l1Accepted' || kind === 'terminalReviewAccepted'
|
|
84
|
+
? kind
|
|
85
|
+
: null;
|
|
86
|
+
}
|
|
73
87
|
export function isAgentCapacityRecoveryCall(input) {
|
|
74
88
|
return (Number.isSafeInteger(input.id) &&
|
|
75
89
|
input.method === 'item/tool/call' &&
|
|
@@ -951,7 +951,11 @@ function asRecord(value) {
|
|
|
951
951
|
return value && typeof value === 'object' ? value : undefined;
|
|
952
952
|
}
|
|
953
953
|
function decodeAgentTaskPath(value) {
|
|
954
|
-
const
|
|
954
|
+
const source = asRecord(value);
|
|
955
|
+
// Codex 0.153 renamed this source discriminator from `subagent` to
|
|
956
|
+
// `subAgent`. Accept both spellings because the task path is the durable
|
|
957
|
+
// correlation between a physical child thread and its Org Plan executor.
|
|
958
|
+
const subagent = asRecord(source?.subAgent) ?? asRecord(source?.subagent);
|
|
955
959
|
const spawn = asRecord(subagent?.thread_spawn);
|
|
956
960
|
const path = boundedString(spawn?.agent_path, 256);
|
|
957
961
|
return path?.startsWith('/') && !path.includes('..') ? path : undefined;
|
|
@@ -52,7 +52,21 @@ export const gestaltOrgPlanAttentionDynamicTool = {
|
|
|
52
52
|
},
|
|
53
53
|
};
|
|
54
54
|
export function parseOrgPlanAttention(value) {
|
|
55
|
-
if (!isRecord(value)
|
|
55
|
+
if (!isRecord(value))
|
|
56
|
+
return null;
|
|
57
|
+
const keys = Object.keys(value);
|
|
58
|
+
if (keys.length === 2 &&
|
|
59
|
+
keys.every((key) => key === 'reason' || key === 'resumeCondition') &&
|
|
60
|
+
isReason(value.reason) &&
|
|
61
|
+
isResumeCondition(value.resumeCondition) &&
|
|
62
|
+
orgPlanAttentionResumeConditionByReason[value.reason] === value.resumeCondition)
|
|
63
|
+
return {
|
|
64
|
+
reason: value.reason,
|
|
65
|
+
summary: `Supervised execution requires human attention (${value.reason}).`,
|
|
66
|
+
requestedAction: `Satisfy the ${value.resumeCondition} resume condition, then resume or disable Autopilot.`,
|
|
67
|
+
resumeCondition: value.resumeCondition,
|
|
68
|
+
};
|
|
69
|
+
if (keys.length !== 4)
|
|
56
70
|
return null;
|
|
57
71
|
if (!isReason(value.reason) ||
|
|
58
72
|
!isBoundedText(value.summary, 600) ||
|