gestalt-mobile 0.18.4 → 0.18.5
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.
|
@@ -90,6 +90,7 @@ export async function composeRelayApp(options) {
|
|
|
90
90
|
const planMeasurementHelperPath = options.planMeasurementHelperPath ?? process.env.GESTALT_MOBILE_ORG_PLAN_HELPER;
|
|
91
91
|
const withPendingInteractions = (session) => (session ? { ...session, pendingInteractions: interactions.list(session.id) } : null);
|
|
92
92
|
const events = new SessionEventBus();
|
|
93
|
+
let notifyAutopilotSettled = () => undefined;
|
|
93
94
|
const attentionTransitions = {
|
|
94
95
|
subscribe: (sessionId, listener) => events.subscribe(sessionId, (event) => {
|
|
95
96
|
if (event.type !== 'org-plan.attention-required' &&
|
|
@@ -110,7 +111,13 @@ export async function composeRelayApp(options) {
|
|
|
110
111
|
}),
|
|
111
112
|
};
|
|
112
113
|
options.onAttentionTransitions?.(attentionTransitions);
|
|
113
|
-
const activity = new AgentActivityRegistry((snapshot, occurredAt) =>
|
|
114
|
+
const activity = new AgentActivityRegistry((snapshot, occurredAt) => {
|
|
115
|
+
events.publish(journal.append(snapshot.sessionId, 'agent.activity.updated', snapshot, occurredAt));
|
|
116
|
+
if (snapshot.confidence === 'fresh' &&
|
|
117
|
+
['idle', 'blocked'].includes(snapshot.root.state) &&
|
|
118
|
+
['idle', 'blocked'].includes(snapshot.aggregateSubagents))
|
|
119
|
+
notifyAutopilotSettled(snapshot.sessionId);
|
|
120
|
+
}, {
|
|
114
121
|
// Evidence arms one bounded reconciliation; healthy sessions are never polled.
|
|
115
122
|
schedule: options.activitySchedule ??
|
|
116
123
|
((callback, delayMs) => {
|
|
@@ -209,6 +216,7 @@ export async function composeRelayApp(options) {
|
|
|
209
216
|
events.publish(journal.append(sessionId, type, payload, occurredAt, outboxId));
|
|
210
217
|
},
|
|
211
218
|
});
|
|
219
|
+
notifyAutopilotSettled = (sessionId) => autopilot.activitySettled(sessionId);
|
|
212
220
|
options.onAutopilotCoordinator?.(autopilot);
|
|
213
221
|
const workspaces = new FilesystemWorkspaceCatalog(root);
|
|
214
222
|
const models = new CodexModelCatalog(root, options.launchAppServer ?? launchCodexAppServer);
|
|
@@ -38,7 +38,9 @@ export function decideAutopilot(input) {
|
|
|
38
38
|
return { kind: 'reconcile' };
|
|
39
39
|
if (Date.parse(now) - Date.parse(activity.root.lastActivityAt) > policy.staleAfterMs)
|
|
40
40
|
return { kind: 'reconcile' };
|
|
41
|
-
|
|
41
|
+
const rootSettled = activity.root.state === 'idle' || activity.root.state === 'blocked';
|
|
42
|
+
const subagentsSettled = activity.aggregateSubagents === 'idle' || activity.aggregateSubagents === 'blocked';
|
|
43
|
+
if (!rootSettled || !subagentsSettled)
|
|
42
44
|
return activity.aggregateSubagents === 'disconnected'
|
|
43
45
|
? { kind: 'reconcile' }
|
|
44
46
|
: { kind: 'observe' };
|
|
@@ -145,20 +145,8 @@ export class AutopilotCoordinator {
|
|
|
145
145
|
const prior = this.deps.store.find(sessionId);
|
|
146
146
|
if (!prior)
|
|
147
147
|
return this.snapshot(sessionId);
|
|
148
|
-
const plan = this.deps.plan(sessionId);
|
|
149
148
|
const session = this.deps.session(sessionId);
|
|
150
|
-
const decision =
|
|
151
|
-
state: prior,
|
|
152
|
-
plan: plan?.plan ?? null,
|
|
153
|
-
planIdentity: plan?.identity,
|
|
154
|
-
planFingerprint: plan ? fingerprint(plan.plan) : null,
|
|
155
|
-
activity: this.deps.activity(sessionId),
|
|
156
|
-
hasPendingInteraction: this.deps.pendingInteraction(sessionId),
|
|
157
|
-
hasActiveAttention: prior.state === 'attentionRequired',
|
|
158
|
-
lastTurnOutcome: this.lastTurnOutcome(sessionId, prior.lastControlId),
|
|
159
|
-
now: this.deps.now(),
|
|
160
|
-
policy: this.deps.policy,
|
|
161
|
-
});
|
|
149
|
+
const decision = this.decision(sessionId, prior);
|
|
162
150
|
const now = this.deps.now();
|
|
163
151
|
this.deps.diagnostic?.(sessionId, decision.kind);
|
|
164
152
|
let next = prior;
|
|
@@ -172,6 +160,21 @@ export class AutopilotCoordinator {
|
|
|
172
160
|
case 'scheduleContinuation':
|
|
173
161
|
if (session?.activeTurnId || this.deps.pendingInteraction(sessionId))
|
|
174
162
|
break;
|
|
163
|
+
const existingControl = prior.lastControlId
|
|
164
|
+
? this.deps.store.findControl(sessionId, prior.lastControlId)
|
|
165
|
+
: null;
|
|
166
|
+
if (existingControl?.status === 'scheduled') {
|
|
167
|
+
const evaluationAt = prior.nextEvaluationAt ?? decision.at;
|
|
168
|
+
if (prior.state !== 'backoff' || prior.nextEvaluationAt !== evaluationAt)
|
|
169
|
+
this.persist({
|
|
170
|
+
...prior,
|
|
171
|
+
state: 'backoff',
|
|
172
|
+
nextEvaluationAt: evaluationAt,
|
|
173
|
+
updatedAt: now,
|
|
174
|
+
});
|
|
175
|
+
this.arm(sessionId, prior.generation, evaluationAt);
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
175
178
|
const scheduledControlId = this.deps.nextControlId(sessionId, prior.generation);
|
|
176
179
|
next = {
|
|
177
180
|
...prior,
|
|
@@ -272,9 +275,18 @@ export class AutopilotCoordinator {
|
|
|
272
275
|
this.evaluate(sessionId);
|
|
273
276
|
}
|
|
274
277
|
turnCompleted(sessionId) {
|
|
278
|
+
this.activitySettled(sessionId);
|
|
279
|
+
}
|
|
280
|
+
activitySettled(sessionId) {
|
|
281
|
+
const prior = this.deps.store.find(sessionId);
|
|
282
|
+
if (!prior?.requestedEnabled || prior.state !== 'monitoring')
|
|
283
|
+
return;
|
|
275
284
|
this.completionTimers.get(sessionId)?.();
|
|
276
285
|
this.completionTimers.set(sessionId, this.deps.schedule(() => {
|
|
277
286
|
this.completionTimers.delete(sessionId);
|
|
287
|
+
const current = this.deps.store.find(sessionId);
|
|
288
|
+
if (!current?.requestedEnabled || current.state !== 'monitoring')
|
|
289
|
+
return;
|
|
278
290
|
this.evaluate(sessionId);
|
|
279
291
|
}, this.deps.policy.quiescenceMs));
|
|
280
292
|
}
|
|
@@ -339,13 +351,21 @@ export class AutopilotCoordinator {
|
|
|
339
351
|
async fire(sessionId, generation) {
|
|
340
352
|
this.timers.delete(sessionId);
|
|
341
353
|
const current = this.deps.store.find(sessionId);
|
|
354
|
+
if (!current || current.generation !== generation || !current.requestedEnabled)
|
|
355
|
+
return;
|
|
342
356
|
const session = this.deps.session(sessionId);
|
|
343
|
-
if (
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
357
|
+
if (session?.activeTurnId ||
|
|
358
|
+
this.deps.pendingInteraction(sessionId) ||
|
|
359
|
+
this.decision(sessionId, current).kind !== 'scheduleContinuation') {
|
|
360
|
+
this.persist({
|
|
361
|
+
...current,
|
|
362
|
+
state: 'monitoring',
|
|
363
|
+
nextEvaluationAt: null,
|
|
364
|
+
updatedAt: this.deps.now(),
|
|
365
|
+
});
|
|
366
|
+
this.evaluate(sessionId);
|
|
348
367
|
return;
|
|
368
|
+
}
|
|
349
369
|
const id = current.lastControlId;
|
|
350
370
|
if (!id)
|
|
351
371
|
return;
|
|
@@ -481,6 +501,21 @@ export class AutopilotCoordinator {
|
|
|
481
501
|
return 'completed';
|
|
482
502
|
return 'unknown';
|
|
483
503
|
}
|
|
504
|
+
decision(sessionId, state) {
|
|
505
|
+
const plan = this.deps.plan(sessionId);
|
|
506
|
+
return decideAutopilot({
|
|
507
|
+
state,
|
|
508
|
+
plan: plan?.plan ?? null,
|
|
509
|
+
planIdentity: plan?.identity,
|
|
510
|
+
planFingerprint: plan ? fingerprint(plan.plan) : null,
|
|
511
|
+
activity: this.deps.activity(sessionId),
|
|
512
|
+
hasPendingInteraction: this.deps.pendingInteraction(sessionId),
|
|
513
|
+
hasActiveAttention: state.state === 'attentionRequired',
|
|
514
|
+
lastTurnOutcome: this.lastTurnOutcome(sessionId, state.lastControlId),
|
|
515
|
+
now: this.deps.now(),
|
|
516
|
+
policy: this.deps.policy,
|
|
517
|
+
});
|
|
518
|
+
}
|
|
484
519
|
async reconcile(sessionId, generation) {
|
|
485
520
|
try {
|
|
486
521
|
const result = await this.deps.reconcile(sessionId);
|
|
@@ -20,6 +20,14 @@ export const orgPlanAttentionResumeConditions = [
|
|
|
20
20
|
'permissionGranted',
|
|
21
21
|
'externalStateChanged',
|
|
22
22
|
];
|
|
23
|
+
export const orgPlanAttentionResumeConditionByReason = {
|
|
24
|
+
planChange: 'planRevision',
|
|
25
|
+
hardBlock: 'externalStateChanged',
|
|
26
|
+
missingDependency: 'dependencyInstalled',
|
|
27
|
+
permissionRequired: 'permissionGranted',
|
|
28
|
+
externalState: 'externalStateChanged',
|
|
29
|
+
materialAmbiguity: 'userGuidance',
|
|
30
|
+
};
|
|
23
31
|
export const gestaltOrgPlanAttentionDynamicTool = {
|
|
24
32
|
type: 'function',
|
|
25
33
|
name: GESTALT_ORG_PLAN_ATTENTION_TOOL_NAME,
|
|
@@ -34,6 +42,13 @@ export const gestaltOrgPlanAttentionDynamicTool = {
|
|
|
34
42
|
requestedAction: { type: 'string', minLength: 1, maxLength: 600 },
|
|
35
43
|
resumeCondition: { type: 'string', enum: orgPlanAttentionResumeConditions },
|
|
36
44
|
},
|
|
45
|
+
oneOf: orgPlanAttentionReasons.map((reason) => ({
|
|
46
|
+
properties: {
|
|
47
|
+
reason: { const: reason },
|
|
48
|
+
resumeCondition: { const: orgPlanAttentionResumeConditionByReason[reason] },
|
|
49
|
+
},
|
|
50
|
+
required: ['reason', 'resumeCondition'],
|
|
51
|
+
})),
|
|
37
52
|
},
|
|
38
53
|
};
|
|
39
54
|
export function parseOrgPlanAttention(value) {
|
|
@@ -42,7 +57,8 @@ export function parseOrgPlanAttention(value) {
|
|
|
42
57
|
if (!isReason(value.reason) ||
|
|
43
58
|
!isBoundedText(value.summary, 600) ||
|
|
44
59
|
!isBoundedText(value.requestedAction, 600) ||
|
|
45
|
-
!isResumeCondition(value.resumeCondition)
|
|
60
|
+
!isResumeCondition(value.resumeCondition) ||
|
|
61
|
+
orgPlanAttentionResumeConditionByReason[value.reason] !== value.resumeCondition)
|
|
46
62
|
return null;
|
|
47
63
|
return {
|
|
48
64
|
reason: value.reason,
|