@awak-app/simy-cli 0.7.0 → 0.7.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/README.md
CHANGED
|
@@ -1042,6 +1042,12 @@ remaining task findings. `runtime legacy-hooks` routes only the exact shipped
|
|
|
1042
1042
|
customized hooks and trust settings remain unchanged. `runtime workflows` refreshes
|
|
1043
1043
|
existing provider workflows without enabling them.
|
|
1044
1044
|
|
|
1045
|
+
`runtime doctor` also returns `taskReadiness` for every existing task: its original
|
|
1046
|
+
ID/state, configuration compatibility or pending migration, and the reason work is
|
|
1047
|
+
queued or waiting. `current` describes configuration only; it does not certify a
|
|
1048
|
+
worker execution, successful repair, or acceptance. User stops and OFF remain
|
|
1049
|
+
visible rather than being counted as migration failures.
|
|
1050
|
+
|
|
1045
1051
|
For existing opted-in Codex tasks, the controller verifies original session, cwd,
|
|
1046
1052
|
model, provider link and consent epoch before an idempotent policy migration.
|
|
1047
1053
|
Frozen goal, criteria, grants, budgets and failure history are retained. Observable
|
|
@@ -1058,6 +1064,13 @@ recorded; an audit receipt does not complete a task. SQM evidence stays bound to
|
|
|
1058
1064
|
repository/commit/diff scope and does not prove the whole user outcome. Git output
|
|
1059
1065
|
no longer uses a 10 MiB `execFile` buffer; the full diff is still retained in memory.
|
|
1060
1066
|
|
|
1067
|
+
If a later audit finds unmet work while an earlier review was waiting only for
|
|
1068
|
+
human acceptance, the controller reopens independent inspection of the durable
|
|
1069
|
+
findings. Confirmed gaps return to diagnosis, authorized repair and reinspection;
|
|
1070
|
+
incorrect findings need captured counterevidence to be refuted. Restarting the host
|
|
1071
|
+
retains this handoff, task identity and budgets. Actual human acceptance is never
|
|
1072
|
+
supplied by the repair loop, and unresolved native permissions are preserved.
|
|
1073
|
+
|
|
1061
1074
|
Codex cold recovery and independent audit are supported. Claude retains its
|
|
1062
1075
|
original live-hook boundary; cold recovery and detached independent audit are not
|
|
1063
1076
|
implemented in this release and are exposed as a capability gap. Neither an
|
package/package.json
CHANGED
package/src/cli-contract.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from "./durable-local-task-steps-contract.js";
|
|
14
14
|
import { buildExecutionCapabilityContract } from "./execution-capability-contract.js";
|
|
15
15
|
|
|
16
|
-
export const CLI_VERSION = "0.7.
|
|
16
|
+
export const CLI_VERSION = "0.7.1";
|
|
17
17
|
export const CLI_API_CONTRACT_VERSION = 10;
|
|
18
18
|
|
|
19
19
|
export function narrativeImageCapabilityMetadata() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { diagnoseDelivery } from './diagnosis.js';
|
|
2
2
|
import { linkSessionParent } from '../session-supervision/session-lineage.js';
|
|
3
|
-
import { adoptAutonomousPolicy } from '../session-supervision/autonomous-upgrade.js';
|
|
3
|
+
import { adoptAutonomousPolicy, autonomousTaskReadiness } from '../session-supervision/autonomous-upgrade.js';
|
|
4
4
|
import { deliveryEvidence } from './evidence.js';
|
|
5
5
|
import { readFile } from 'node:fs/promises';
|
|
6
6
|
import { startDelivery, checkpointDelivery, controlDelivery, listRecords, rootDirectory, verifyDelivery, readRecord } from '../codex-plugins/simy/hooks/delivery-loop.mjs';
|
|
@@ -80,7 +80,8 @@ export async function runDeliveryCommand(argv, { root = rootDirectory(), cwd = p
|
|
|
80
80
|
if (args[0] === 'doctor') {
|
|
81
81
|
const [runtime, host, monitor, records] = await Promise.all([localRuntimeStatus({ root: dirname(root), allowMissingLock: true }),
|
|
82
82
|
desktopRuntimeStatus({ root: dirname(root) }), consistencyMonitorStatus(root), listRecords(root)]);
|
|
83
|
-
output(JSON.stringify({ runtime, host, monitor, tasks: records.flatMap(record => consistencyFindings(record))
|
|
83
|
+
output(JSON.stringify({ runtime, host, monitor, tasks: records.flatMap(record => consistencyFindings(record)),
|
|
84
|
+
taskReadiness: records.map(record => autonomousTaskReadiness(record)) }, null, 2)); return 0;
|
|
84
85
|
}
|
|
85
86
|
const result = args[0] === 'workflows' ? await syncInstalledWorkflows({ installMissing: false })
|
|
86
87
|
: await (args[0] === 'install' ? installLocalRuntime : localRuntimeStatus)({ root: dirname(root), ...(args[0] === 'status' ? { allowMissingLock: true } : {}) });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
-
import { automaticContinuationAllowed, listRecords, updateRecord, queuePurposeDiagnosis, queuePurposeVerification } from '../codex-plugins/simy/hooks/delivery-loop.mjs';
|
|
2
|
+
import { automaticContinuationAllowed, listRecords, updateRecord, queuePurposeDiagnosis, queuePurposeVerification, managedBudgetReason } from '../codex-plugins/simy/hooks/delivery-loop.mjs';
|
|
3
3
|
import { autonomousPolicyAuthorized, policyContractIdentity, acceptanceIdentity, legacyIndependentCriterion, effectiveAcceptanceCriteria } from '../codex-plugins/simy/hooks/acceptance-policy.mjs';
|
|
4
4
|
import { purposeFailureMode } from '../codex-plugins/simy/hooks/purpose-search.mjs';
|
|
5
5
|
|
|
@@ -28,6 +28,34 @@ export function policyAdoptionPending(record) {
|
|
|
28
28
|
(m.acceptanceRoutes?.[c.id]?.source !== 'explicit_user_runtime_policy' || m.acceptanceRoutes[c.id].authorizationId !== m.policyAuthorization.id || m.acceptanceRoutes[c.id].criterionDigest !== acceptanceIdentity(c))));
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
// Read-only rollout accounting. Configuration compatibility is not evidence of
|
|
32
|
+
// a dispatched worker, successful repair, or accepted user outcome.
|
|
33
|
+
export function autonomousTaskReadiness(record, now = Date.now()) {
|
|
34
|
+
const run = record?.run, m = run?.managed;
|
|
35
|
+
const result = { provider: record.provider, session: record.session, taskId: m?.taskId || null,
|
|
36
|
+
state: run?.state || 'not_started', configuration: 'not_applicable', continuation: 'not_enrolled' };
|
|
37
|
+
if (!m || record.provider !== 'codex') return result;
|
|
38
|
+
const current = m.purpose?.enabled === true && m.purpose.version === 2 && m.purpose.decisionPolicy === 'llm-first-hc102' &&
|
|
39
|
+
m.purpose.failureBudgetMode === purposeFailureMode(record) && m.controllerContractVersion === AUTONOMOUS_STATE_CONTRACT &&
|
|
40
|
+
m.resumeChannel === 'codex_app_server' && !policyAdoptionPending(record);
|
|
41
|
+
result.configuration = current ? 'current' : 'migration_pending';
|
|
42
|
+
if (record.providerArchived) result.continuation = 'archived';
|
|
43
|
+
else if (run.state === 'stopped' || run.stopReason?.actor === 'user' || run.state === 'paused' && pauseOrigin(run) === 'user') result.continuation = 'user_stopped';
|
|
44
|
+
else if (!record.autoContinue || !automaticContinuationAllowed(record)) result.continuation = 'off';
|
|
45
|
+
else if (['completed', 'reported_done', 'failed', 'budget_exhausted'].includes(run.state)) result.continuation = run.state;
|
|
46
|
+
else if (!current) result.continuation = 'migration_pending';
|
|
47
|
+
else if (m.usageBlocked || managedBudgetReason(run, now)) result.continuation = 'budget_boundary';
|
|
48
|
+
else if (m.pendingApproval || m.nativeWait || m.purpose.nativeBoundary || m.executionUncertain || m.providerWarning) result.continuation = 'authority_or_execution_boundary';
|
|
49
|
+
else if (['pending', 'transferred', 'pending_received'].includes(m.handoff?.status)) result.continuation = 'handoff_pending';
|
|
50
|
+
else if (m.resumeLease || m.liveWorkspaceLease || m.outcomeAudit?.lease) result.continuation = 'owned_attempt';
|
|
51
|
+
else if (run.state === 'verification_pending') {
|
|
52
|
+
const findings = m.purpose.evidence?.findings?.filter(row => row.status === 'open' && row.owner !== 'human') || [];
|
|
53
|
+
const pending = effectiveAcceptanceCriteria(m).filter(c => !m.verified?.[c.id]);
|
|
54
|
+
result.continuation = findings.length ? 'outcome_reinspection_pending' : pending.length && pending.every(c => c.check.type === 'manual') ? 'manual_acceptance' : 'verification_pending';
|
|
55
|
+
} else result.continuation = run.state === 'recovery_required' && m.autoRecovery ? 'recovery_queued' : run.state;
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
|
|
31
59
|
// Controller-only, idempotent migration. Frozen criteria and grants remain
|
|
32
60
|
// byte-identical, including each criterion's original verifier.
|
|
33
61
|
export function upgradeAutonomousRecord(record, { rules, now, metadata } = {}) {
|
|
@@ -2,7 +2,7 @@ import { preparePurposeCandidate } from './purpose-candidate.js';
|
|
|
2
2
|
import { requireProviderLink } from '../provider-links.js';
|
|
3
3
|
import { searchEnabled, purposeFailureMode } from '../codex-plugins/simy/hooks/purpose-search.mjs';
|
|
4
4
|
import { createHash } from 'node:crypto';
|
|
5
|
-
import { listRecords, readRecord, updateRecord, automaticContinuationAllowed, queuePurposeDiagnosis, queuePurposeVerification } from '../codex-plugins/simy/hooks/delivery-loop.mjs';
|
|
5
|
+
import { listRecords, readRecord, updateRecord, automaticContinuationAllowed, queuePurposeDiagnosis, queuePurposeVerification, managedBudgetReason } from '../codex-plugins/simy/hooks/delivery-loop.mjs';
|
|
6
6
|
import { openCodexAppServer, readCodexSession, observeCodexAuthentication } from './codex-app-server.js';
|
|
7
7
|
import { realpath } from 'node:fs/promises';
|
|
8
8
|
import { loadPurposeRuleSet } from './purpose-instruction.js';
|
|
@@ -10,6 +10,16 @@ import { purposeEnabled } from '../codex-plugins/simy/hooks/purpose-cycle.mjs';
|
|
|
10
10
|
import { upgradeAutonomousRecord, upgradeEligible, policyAdoptionPending } from './autonomous-upgrade.js';
|
|
11
11
|
import { effectiveAcceptanceCriteria } from '../codex-plugins/simy/hooks/acceptance-policy.mjs';
|
|
12
12
|
|
|
13
|
+
function auditReinspectionEligible(run, now) {
|
|
14
|
+
const m = run?.managed;
|
|
15
|
+
return run?.state === 'verification_pending' && searchEnabled(m) &&
|
|
16
|
+
m.purpose.evidence?.findings?.some(row => row.status === 'open' && row.owner !== 'human') &&
|
|
17
|
+
!m.resumeLease && !m.liveWorkspaceLease && !m.outcomeAudit?.lease &&
|
|
18
|
+
!m.pendingApproval && !m.nativeWait && !m.purpose.nativeBoundary && !m.executionUncertain &&
|
|
19
|
+
!m.providerWarning && !m.usageBlocked && !managedBudgetReason(run, now) &&
|
|
20
|
+
!['pending', 'transferred', 'pending_received'].includes(m.handoff?.status);
|
|
21
|
+
}
|
|
22
|
+
|
|
13
23
|
export async function observePurposeTurn(record, { openClient = openCodexAppServer } = {}) {
|
|
14
24
|
const client = await openClient();
|
|
15
25
|
try {
|
|
@@ -41,6 +51,7 @@ export async function observeUpgradeMetadata(record, { openClient = openCodexApp
|
|
|
41
51
|
}
|
|
42
52
|
|
|
43
53
|
export async function reconcilePurposeTasks({ root, signal, prepareCandidate = preparePurposeCandidate, now = Date.now(), observeTurn = observePurposeTurn, observeAuthentication = observeCodexAuthentication, readRecords = () => listRecords(root), observeMetadata = observeUpgradeMetadata }) {
|
|
54
|
+
const started = Date.now();
|
|
44
55
|
let rules;
|
|
45
56
|
const deferred = [];
|
|
46
57
|
|
|
@@ -234,6 +245,24 @@ export async function reconcilePurposeTasks({ root, signal, prepareCandidate = p
|
|
|
234
245
|
obstacle ||= { type: run.blocker, detail: run.managed.understandingQuestion?.text || run.managed.userQuestion ||
|
|
235
246
|
run.managed.dependencyWait?.condition || run.managed.pendingApproval?.summary || run.reason };
|
|
236
247
|
} else if (run.state === 'verification_pending') {
|
|
248
|
+
// A later independent audit can invalidate an earlier "human only"
|
|
249
|
+
// disposition. Reinspect its durable findings through the canonical
|
|
250
|
+
// verifier before assigning repairs; a report is neither a pass nor an
|
|
251
|
+
// authorization to replay an effect. This also recovers the handoff if
|
|
252
|
+
// the host exited after persisting findings but before scheduling work.
|
|
253
|
+
if (auditReinspectionEligible(run, now)) {
|
|
254
|
+
await persist(current => {
|
|
255
|
+
const inspectionNow = now + Math.max(0, Date.now() - started);
|
|
256
|
+
// Auditing can change findings, authority and usage without advancing
|
|
257
|
+
// the checkpoint CAS. Recheck the full predicate under the state lock.
|
|
258
|
+
if (current?.run?.id !== run.id || current.run.revision !== run.revision || current.run.state !== run.state || current.run.updatedAt !== run.updatedAt ||
|
|
259
|
+
!auditReinspectionEligible(current.run, inspectionNow)) return current;
|
|
260
|
+
queuePurposeVerification(current.run, 'Independent audit left unresolved outcome findings. Reinspect original requirements and current primary evidence; repair genuine gaps or refute incorrect findings with bound counterevidence. Preserve explicit human acceptance.', inspectionNow);
|
|
261
|
+
current.run.managed.nextAttemptAt = inspectionNow;
|
|
262
|
+
return current;
|
|
263
|
+
});
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
237
266
|
if (searchEnabled(run.managed) && (run.managed.purpose.search?.review?.passed || run.managed.purpose.search?.review?.needsHuman) && effectiveAcceptanceCriteria(run.managed).filter(c => !run.managed.verified[c.id]).every(c => c.check.type === 'manual')) continue;
|
|
238
267
|
obstacle = { type: 'verification', detail: run.managed.acceptanceCriteria.filter(c => !run.managed.verified[c.id]).map(c => c.id).join(',') };
|
|
239
268
|
} else if (run.state === 'recovery_required' && ['provider_error', 'process_exit', 'shutdown_cancelled', 'interrupted_owner'].includes(run.recoveryCause) &&
|