gestalt-mobile 0.42.0 → 0.42.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.
|
@@ -102,6 +102,10 @@ export class AgentActivityRegistry {
|
|
|
102
102
|
kind: 'collaboration',
|
|
103
103
|
childId: child.id,
|
|
104
104
|
childStatus: 'notLoaded',
|
|
105
|
+
// The bounded roster/process read is authoritative as a unit. If a
|
|
106
|
+
// child disappeared, its previous runtime-local process projection
|
|
107
|
+
// cannot remain eligible as live work after this reconciliation.
|
|
108
|
+
childOwnedProcesses: [],
|
|
105
109
|
});
|
|
106
110
|
// A malformed/unknown `thread/list` row is not proof that a child is
|
|
107
111
|
// healthy. Preserve it as disconnected but keep the aggregate qualified
|
|
@@ -49,6 +49,47 @@ export function classifyAgentActivity(activity) {
|
|
|
49
49
|
const subagentsSettled = activity.aggregateSubagents === 'idle' || activity.aggregateSubagents === 'blocked';
|
|
50
50
|
return rootSettled && subagentsSettled ? 'settled' : 'observe';
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Classifies actor state against the plan position that can legally run next.
|
|
54
|
+
* Completed canonical executors remain visible evidence, but their terminal
|
|
55
|
+
* topology cannot indefinitely turn a fresh read into a reconciliation loop.
|
|
56
|
+
*/
|
|
57
|
+
function classifySupervisedActivity(activity, plan) {
|
|
58
|
+
const selected = plan.steps.findIndex((step) => step.id === plan.currentStepId || step.state === 'WIP');
|
|
59
|
+
const index = selected >= 0 ? selected : plan.steps.findIndex((step) => step.state !== 'DONE');
|
|
60
|
+
if (index < 0)
|
|
61
|
+
return classifyAgentActivity(activity);
|
|
62
|
+
const position = index + 1;
|
|
63
|
+
const retained = activity.subagents.filter((child) => {
|
|
64
|
+
const match = /^L([1-9]\d*)$/.exec(child.canonicalPosition ?? '');
|
|
65
|
+
if (!match)
|
|
66
|
+
return true;
|
|
67
|
+
const historical = Number(match[1]) < position;
|
|
68
|
+
const supervisedProcess = child.ownedProcesses?.some((process) => process.ownership === 'supervisor' &&
|
|
69
|
+
(process.state === 'running' || process.state === 'detached-active'));
|
|
70
|
+
return !historical || supervisedProcess;
|
|
71
|
+
});
|
|
72
|
+
// An aggregate-only observation cannot prove which child is historical.
|
|
73
|
+
if (retained.length === activity.subagents.length)
|
|
74
|
+
return classifyAgentActivity(activity);
|
|
75
|
+
if (activity.root.state === 'awaitingHuman' ||
|
|
76
|
+
retained.some((child) => child.state === 'awaitingHuman'))
|
|
77
|
+
return 'attention';
|
|
78
|
+
if (activity.root.state === 'working' ||
|
|
79
|
+
retained.some((child) => child.state === 'working' ||
|
|
80
|
+
child.state === 'awaitingAgent' ||
|
|
81
|
+
child.ownedProcesses?.some((process) => (child.state !== 'disconnected' || process.ownership === 'supervisor') &&
|
|
82
|
+
(process.state === 'running' || process.state === 'detached-active'))))
|
|
83
|
+
return 'active';
|
|
84
|
+
if (activity.root.state === 'disconnected' ||
|
|
85
|
+
retained.some((child) => child.state === 'disconnected'))
|
|
86
|
+
return 'reconcile';
|
|
87
|
+
const rootSettled = activity.root.state === 'idle' ||
|
|
88
|
+
activity.root.state === 'blocked' ||
|
|
89
|
+
activity.root.state === 'awaitingAgent';
|
|
90
|
+
const childrenSettled = retained.every((child) => child.state === 'idle' || child.state === 'blocked');
|
|
91
|
+
return rootSettled && childrenSettled ? 'settled' : 'observe';
|
|
92
|
+
}
|
|
52
93
|
/** A deliberately pure, exhaustive safety gate. Adapters may only enact this result. */
|
|
53
94
|
export function decideAutopilot(input) {
|
|
54
95
|
const { state, plan, activity, hasPendingInteraction, now, policy } = input;
|
|
@@ -93,7 +134,7 @@ export function decideAutopilot(input) {
|
|
|
93
134
|
return { kind: 'reconcile' };
|
|
94
135
|
if (Date.parse(now) - Date.parse(activity.root.observedAt) > policy.staleAfterMs)
|
|
95
136
|
return { kind: 'reconcile' };
|
|
96
|
-
const disposition =
|
|
137
|
+
const disposition = classifySupervisedActivity(activity, plan);
|
|
97
138
|
if (disposition === 'attention')
|
|
98
139
|
return { kind: 'observe' };
|
|
99
140
|
if (disposition === 'reconcile')
|