create-principles-disciple 1.145.28 → 1.145.29
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/_release/product-identity.json +2 -2
- package/codex-adapter/dist/index.d.ts +4 -0
- package/codex-adapter/dist/index.js +2 -0
- package/codex-adapter/dist/ingestion/admission.js +1 -0
- package/codex-adapter/dist/ingestion/explicit-owner-statement.d.ts +22 -3
- package/codex-adapter/dist/ingestion/explicit-owner-statement.js +43 -16
- package/codex-adapter/dist/ingestion/turn-context.d.ts +75 -0
- package/codex-adapter/dist/ingestion/turn-context.js +302 -0
- package/codex-adapter/dist/pd-hook.js +87 -36
- package/codex-adapter/dist/worker/workspace-worker.js +4 -1
- package/codex-adapter/package.json +1 -1
- package/core/dist/runtime-v2/context-payload.d.ts +4 -4
- package/core/dist/runtime-v2/context-payload.d.ts.map +1 -1
- package/core/dist/runtime-v2/context-payload.js +1 -0
- package/core/dist/runtime-v2/context-payload.js.map +1 -1
- package/core/dist/runtime-v2/task-status.d.ts +1 -1
- package/core/package.json +1 -1
- package/host-runtime/dist/governance-signal-admission.d.ts +26 -7
- package/host-runtime/dist/governance-signal-admission.js +104 -34
- package/host-runtime/package.json +1 -1
- package/package.json +2 -2
- package/pd-cli/dist/commands/codex-reconcile.d.ts.map +1 -1
- package/pd-cli/dist/commands/codex-reconcile.js +30 -1
- package/pd-cli/dist/commands/codex-reconcile.js.map +1 -1
- package/pd-cli/dist/commands/pain-explicit.d.ts +8 -0
- package/pd-cli/dist/commands/pain-explicit.d.ts.map +1 -0
- package/pd-cli/dist/commands/pain-explicit.js +89 -0
- package/pd-cli/dist/commands/pain-explicit.js.map +1 -0
- package/pd-cli/dist/index.js +10 -0
- package/pd-cli/dist/index.js.map +1 -1
- package/pd-cli/package.json +1 -1
- package/plugin/dist/bundle.js +1 -1
- package/plugin/openclaw.plugin.json +1 -1
- package/plugin/package.json +1 -1
- package/release-manager/package.json +2 -2
- package/workbuddy-adapter/dist/worker/workspace-worker.js +1 -1
- package/workbuddy-adapter/package.json +1 -1
|
@@ -395,7 +395,9 @@ function tryConsumeRateLimit({ db, rootSessionId, ruleVersion, now }) {
|
|
|
395
395
|
}
|
|
396
396
|
function insertCorrectionPain({ db, candidate, workspaceDir, painId, detection, nowIso }) {
|
|
397
397
|
const reason = candidate.explicitOwnerStatement === true
|
|
398
|
-
?
|
|
398
|
+
? candidate.explicitChannel === 'codex_explicit_agent'
|
|
399
|
+
? 'Codex explicit owner statement (agent-assisted, no prefix)'
|
|
400
|
+
: `${candidate.hostKind === 'workbuddy' ? 'WorkBuddy' : 'Codex'} explicit owner statement (PD记录痛苦 prefix)`
|
|
399
401
|
: detection !== undefined && detection.matchedTerms.length > 0
|
|
400
402
|
? `User correction detected: ${detection.matchedTerms.join(', ')}`
|
|
401
403
|
: 'User correction detected';
|
|
@@ -461,7 +463,7 @@ function taskSubmissionPayload(candidate, workspaceDir, details) {
|
|
|
461
463
|
...(candidate.explicitOwnerStatement === true
|
|
462
464
|
? { correctionEvidence: candidate.hostKind === 'workbuddy'
|
|
463
465
|
? { text: boundCorrectionEvidenceText({ text: candidate.text, classification: 'owner_statement', channel: 'workbuddy_explicit_prefix' }, workspaceDir).text, classification: 'owner_statement', channel: 'workbuddy_explicit_prefix' }
|
|
464
|
-
: { text: sanitizeString(candidate.text, workspaceDir), classification: 'owner_statement', channel: 'codex_explicit_prefix' } }
|
|
466
|
+
: { text: sanitizeString(candidate.text, workspaceDir), classification: 'owner_statement', channel: candidate.explicitChannel === 'codex_explicit_agent' ? 'codex_explicit_agent' : 'codex_explicit_prefix' } }
|
|
465
467
|
: {}),
|
|
466
468
|
};
|
|
467
469
|
return JSON.stringify(payload);
|
|
@@ -482,6 +484,35 @@ function insertAdmissionMarker({ db, identity, canonicalPainId, ruleVersion, rea
|
|
|
482
484
|
VALUES (?, ?, ?, ?, ?, 'admitted', ?, NULL, ?, ?, ?, ?, ?)`)
|
|
483
485
|
.run(identity.hostKind, identity.logicalObservationKey, identity.rolloutIdentity, identity.rootSessionId, identity.signalKind, canonicalPainId, ruleVersion, reason.slice(0, MAX_REASON_BOUND) || null, payloadJson, nowIso, nowIso);
|
|
484
486
|
}
|
|
487
|
+
/**
|
|
488
|
+
* Convergence probe for the concurrent-redelivery race: two admission
|
|
489
|
+
* processes passed the pre-transaction marker probe before either committed,
|
|
490
|
+
* so the loser's marker INSERT hits the store UNIQUE constraint and its whole
|
|
491
|
+
* transaction rolls back. A marker AND its canonical pain now existing means
|
|
492
|
+
* the idempotency contract already WON — converge on the winner's state (the
|
|
493
|
+
* same semantics as the task-ensure race in ensureGovernanceDiagnosticianTask)
|
|
494
|
+
* instead of surfacing a governance_admission_failed that contradicts the
|
|
495
|
+
* durable state. The probe re-reads the store fresh (rc-7); when the durable
|
|
496
|
+
* facts do NOT exist it returns null and the original error propagates.
|
|
497
|
+
*/
|
|
498
|
+
function convergeOnAdmittedMarker(args) {
|
|
499
|
+
const marker = args.db.prepare('SELECT * FROM governance_signal_admissions WHERE logical_observation_key = ?').get(args.logicalObservationKey);
|
|
500
|
+
if (!isRecord(marker))
|
|
501
|
+
return null;
|
|
502
|
+
const canonicalPainId = rowField(marker, 'canonical_pain_id');
|
|
503
|
+
const taskId = rowField(marker, 'diagnostician_task_id');
|
|
504
|
+
if (typeof canonicalPainId !== 'string')
|
|
505
|
+
return null;
|
|
506
|
+
if (args.db.prepare('SELECT 1 FROM pain_events WHERE canonical_pain_id = ?').get(canonicalPainId) === undefined)
|
|
507
|
+
return null;
|
|
508
|
+
return {
|
|
509
|
+
disposition: 'already_admitted',
|
|
510
|
+
kind: args.kind,
|
|
511
|
+
logicalObservationKey: args.logicalObservationKey,
|
|
512
|
+
canonicalPainId,
|
|
513
|
+
diagnosticianTaskId: typeof taskId === 'string' ? taskId : null,
|
|
514
|
+
};
|
|
515
|
+
}
|
|
485
516
|
function admitCorrection({ db, candidate, workspaceDir, now, nowIso, keywordStore, onDegradation }) {
|
|
486
517
|
// SPEC-CODEX-PAIN-CLOSURE-001 CR-07: an EXPLICIT Owner statement
|
|
487
518
|
// (`PD记录痛苦:<text>` in the current Codex message) does not pass through
|
|
@@ -672,10 +703,23 @@ function admitOne({ db, candidate, workspaceDir, now, nowIso, keywordStore, onDe
|
|
|
672
703
|
diagnosticianTaskId: typeof taskId === 'string' ? taskId : null,
|
|
673
704
|
};
|
|
674
705
|
}
|
|
675
|
-
|
|
676
|
-
|
|
706
|
+
try {
|
|
707
|
+
if (candidate.kind === 'user_correction') {
|
|
708
|
+
return admitCorrection({ db, candidate, workspaceDir, now, nowIso, ...(keywordStore ? { keywordStore } : {}), ...(onDegradation ? { onDegradation } : {}) });
|
|
709
|
+
}
|
|
710
|
+
return admitToolFailure({ db, candidate, workspaceDir, nowIso });
|
|
711
|
+
}
|
|
712
|
+
catch (error) {
|
|
713
|
+
// RateLimitedSignal never reaches here — admitCorrection converts it to a
|
|
714
|
+
// rate_limited outcome internally. A store-level failure (e.g. the loser
|
|
715
|
+
// of a concurrent redelivery hitting the marker UNIQUE constraint) first
|
|
716
|
+
// gets a fresh convergence probe; only a failure with NO durable
|
|
717
|
+
// idempotency facts propagates as governance_admission_failed.
|
|
718
|
+
const converged = convergeOnAdmittedMarker({ db, logicalObservationKey: candidate.logicalObservationKey, kind: candidate.kind });
|
|
719
|
+
if (converged !== null)
|
|
720
|
+
return converged;
|
|
721
|
+
throw error;
|
|
677
722
|
}
|
|
678
|
-
return admitToolFailure({ db, candidate, workspaceDir, nowIso });
|
|
679
723
|
}
|
|
680
724
|
/**
|
|
681
725
|
* Admit governance signal candidates. Each candidate commits in its own
|
|
@@ -836,7 +880,7 @@ function isGovernanceTaskPayload(value) {
|
|
|
836
880
|
const correctionEvidence = own(value, 'correctionEvidence');
|
|
837
881
|
const hasOwnerStatement = isRecord(correctionEvidence)
|
|
838
882
|
&& own(correctionEvidence, 'classification') === 'owner_statement'
|
|
839
|
-
&& (own(correctionEvidence, 'channel') === 'codex_explicit_prefix' && hostKind === 'codex'
|
|
883
|
+
&& ((own(correctionEvidence, 'channel') === 'codex_explicit_prefix' || own(correctionEvidence, 'channel') === 'codex_explicit_agent') && hostKind === 'codex'
|
|
840
884
|
|| own(correctionEvidence, 'channel') === 'workbuddy_explicit_prefix' && hostKind === 'workbuddy')
|
|
841
885
|
&& typeof own(correctionEvidence, 'text') === 'string'
|
|
842
886
|
&& own(correctionEvidence, 'text').length > 0;
|
|
@@ -908,7 +952,7 @@ export async function ensureGovernanceDiagnosticianTask(input) {
|
|
|
908
952
|
const stateManager = new RuntimeStateManager({ workspaceDir });
|
|
909
953
|
try {
|
|
910
954
|
await stateManager.initialize();
|
|
911
|
-
|
|
955
|
+
let existing = await stateManager.getTask(taskId);
|
|
912
956
|
if (existing === null) {
|
|
913
957
|
const stateDir = path.join(workspaceDir, '.state');
|
|
914
958
|
const config = loadPdConfigForPlugin(workspaceDir);
|
|
@@ -949,7 +993,16 @@ export async function ensureGovernanceDiagnosticianTask(input) {
|
|
|
949
993
|
// worker, so release it immediately (hook processes exit anyway).
|
|
950
994
|
await disposePainSignalBridgesForWorkspace(workspaceDir).catch(() => undefined);
|
|
951
995
|
if (result.status === 'failed') {
|
|
952
|
-
|
|
996
|
+
// Concurrent-ensure race (hook redelivery × worker reconcile): both
|
|
997
|
+
// callers passed the getTask probe before either committed, and the
|
|
998
|
+
// DETERMINISTIC task id makes the loser's insert hit the store PK.
|
|
999
|
+
// The task existing now is the idempotency contract winning, not a
|
|
1000
|
+
// failure — converge on it (Case-B repair semantics) instead of
|
|
1001
|
+
// reporting a task_submit_failed that contradicts the durable state.
|
|
1002
|
+
existing = await stateManager.getTask(taskId);
|
|
1003
|
+
if (existing === null) {
|
|
1004
|
+
return { ok: false, reason: `task_submit_failed:${(result.message ?? result.failureCategory ?? 'unknown').slice(0, 160)}`, nextAction: 'inspect the diagnostician runtime profile; reconciliation retries task creation without losing the admitted pain' };
|
|
1005
|
+
}
|
|
953
1006
|
}
|
|
954
1007
|
}
|
|
955
1008
|
db.prepare('UPDATE governance_signal_admissions SET diagnostician_task_id = ?, updated_at = ? WHERE logical_observation_key = ?')
|
|
@@ -995,11 +1048,12 @@ export function promoteAdmittedGovernanceEvidence(input) {
|
|
|
995
1048
|
return { ok: true, promoted: result.promoted, tailState: result.tailState };
|
|
996
1049
|
}
|
|
997
1050
|
/**
|
|
998
|
-
* Given an admitted canonical pain (identified by its marker), ensure
|
|
999
|
-
*
|
|
1051
|
+
* Given an admitted canonical pain (identified by its marker), ensure the
|
|
1052
|
+
* Diagnostician task and, only with current conversation-ingestion consent,
|
|
1053
|
+
* the evidence promotion exist idempotently.
|
|
1000
1054
|
* Single ensure path for both fresh admits and crash recovery (already_admitted
|
|
1001
|
-
* redelivery). The marker row carries rollout_identity
|
|
1002
|
-
*
|
|
1055
|
+
* redelivery). The marker row carries rollout_identity; callers must also
|
|
1056
|
+
* provide the current conversation-ingestion consent.
|
|
1003
1057
|
*/
|
|
1004
1058
|
export async function ensureGovernanceContinuation(input) {
|
|
1005
1059
|
const workspaceDir = path.resolve(input.workspaceDir);
|
|
@@ -1016,23 +1070,26 @@ export async function ensureGovernanceContinuation(input) {
|
|
|
1016
1070
|
if (typeof rolloutIdentity !== 'string' || rolloutIdentity.length === 0) {
|
|
1017
1071
|
return { ok: false, reason: 'marker_missing_rollout_identity', nextAction: 're-admit the signal' };
|
|
1018
1072
|
}
|
|
1019
|
-
//
|
|
1073
|
+
// Promote before making a runnable task visible, so a worker racing this
|
|
1074
|
+
// continuation cannot begin diagnosis before the eligible evidence window
|
|
1075
|
+
// has been promoted. A promotion failure is retained as a degradation,
|
|
1076
|
+
// but never prevents the task from being durably ensured.
|
|
1077
|
+
const promoted = input.conversationIngestionEnabled
|
|
1078
|
+
? promoteAdmittedGovernanceEvidence({
|
|
1079
|
+
workspaceDir,
|
|
1080
|
+
rolloutIdentity,
|
|
1081
|
+
triggerLogicalKey: input.logicalObservationKey,
|
|
1082
|
+
canonicalPainId: input.canonicalPainId,
|
|
1083
|
+
})
|
|
1084
|
+
: undefined;
|
|
1020
1085
|
const ensured = await ensureGovernanceDiagnosticianTask({ workspaceDir, logicalObservationKey: input.logicalObservationKey, canonicalPainId: input.canonicalPainId });
|
|
1021
1086
|
if (!ensured.ok) {
|
|
1022
1087
|
return { ok: false, reason: ensured.reason, nextAction: ensured.nextAction };
|
|
1023
1088
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
const promoted = promoteAdmittedGovernanceEvidence({
|
|
1027
|
-
workspaceDir,
|
|
1028
|
-
rolloutIdentity,
|
|
1029
|
-
triggerLogicalKey: input.logicalObservationKey,
|
|
1030
|
-
canonicalPainId: input.canonicalPainId,
|
|
1031
|
-
});
|
|
1032
|
-
if (!promoted.ok) {
|
|
1033
|
-
return { ok: false, reason: promoted.reason, nextAction: promoted.nextAction };
|
|
1089
|
+
if (promoted !== undefined && !promoted.ok) {
|
|
1090
|
+
return { ok: false, reason: promoted.reason, nextAction: promoted.nextAction, taskId: ensured.taskId, taskCreated: ensured.created, linkRepaired: ensured.linkRepaired };
|
|
1034
1091
|
}
|
|
1035
|
-
return { ok: true, taskId: ensured.taskId, taskCreated: ensured.created, linkRepaired: ensured.linkRepaired, promoted: promoted
|
|
1092
|
+
return { ok: true, taskId: ensured.taskId, taskCreated: ensured.created, linkRepaired: ensured.linkRepaired, promoted: promoted?.promoted ?? 0, tailState: promoted?.tailState ?? 'skipped' };
|
|
1036
1093
|
}
|
|
1037
1094
|
finally {
|
|
1038
1095
|
close();
|
|
@@ -1164,10 +1221,13 @@ export async function admitBareHostPains(input) {
|
|
|
1164
1221
|
/**
|
|
1165
1222
|
* Narrow idempotent reconciliation between the trajectory admission markers
|
|
1166
1223
|
* and the Runtime V2 task store (separate SQLite databases — no cross-store
|
|
1167
|
-
* transaction exists, SPEC §13).
|
|
1224
|
+
* transaction exists, SPEC §13). Current conversation-ingestion consent is
|
|
1225
|
+
* mandatory because it controls whether Codex observations may be read and
|
|
1226
|
+
* promoted; task/link recovery remains available when consent is off. Recovers:
|
|
1168
1227
|
* - Case A: pain admitted, crash before task creation → create the task now;
|
|
1169
1228
|
* - Case B: task exists, crash before the link write → repair the link;
|
|
1170
|
-
* - Case C: pain+task, crash before promotion → promote evidence now
|
|
1229
|
+
* - Case C: pain+task, crash before promotion → promote evidence now only
|
|
1230
|
+
* while conversation-ingestion consent is enabled;
|
|
1171
1231
|
* - Case D: promotion started → pending tail → retry completion once; stale
|
|
1172
1232
|
* tails are reported (never silently dropped).
|
|
1173
1233
|
* Not a background worker — Slice C's Companion worker and the CLI call this.
|
|
@@ -1194,7 +1254,8 @@ export async function reconcileGovernanceContinuation(input) {
|
|
|
1194
1254
|
//
|
|
1195
1255
|
// Recovery needed:
|
|
1196
1256
|
// Case A/B: task link missing (crash before task, or before the link write)
|
|
1197
|
-
// Case C: promotion never started (no observation carries this painRef)
|
|
1257
|
+
// Case C: promotion never started (no observation carries this painRef),
|
|
1258
|
+
// but only while current conversation-ingestion consent is on.
|
|
1198
1259
|
//
|
|
1199
1260
|
// Case C/D (promotion recovery) is scoped to the hosts whose evidence
|
|
1200
1261
|
// substrate it exists for: the promotion rows come from transcript
|
|
@@ -1211,14 +1272,17 @@ export async function reconcileGovernanceContinuation(input) {
|
|
|
1211
1272
|
// not retry pending tails, so pending-tail markers do not block later
|
|
1212
1273
|
// markers. Stale tails are NOT auto-recoverable (substrate refuses to
|
|
1213
1274
|
// re-arm them): they are counted separately below and reported.
|
|
1275
|
+
const promotionRecoveryClause = input.conversationIngestionEnabled
|
|
1276
|
+
? `OR (a.host_kind = 'codex'
|
|
1277
|
+
AND NOT EXISTS (SELECT 1 FROM governance_observations o
|
|
1278
|
+
WHERE o.promotion_ref = a.canonical_pain_id))`
|
|
1279
|
+
: '';
|
|
1214
1280
|
const markers = db.prepare(`SELECT a.logical_observation_key, a.canonical_pain_id, a.host_kind
|
|
1215
1281
|
FROM governance_signal_admissions a
|
|
1216
1282
|
WHERE a.decision = 'admitted'
|
|
1217
1283
|
AND (
|
|
1218
1284
|
a.diagnostician_task_id IS NULL
|
|
1219
|
-
|
|
1220
|
-
AND NOT EXISTS (SELECT 1 FROM governance_observations o
|
|
1221
|
-
WHERE o.promotion_ref = a.canonical_pain_id))
|
|
1285
|
+
${promotionRecoveryClause}
|
|
1222
1286
|
)
|
|
1223
1287
|
ORDER BY a.id LIMIT ?`).all(limit);
|
|
1224
1288
|
for (const row of markers) {
|
|
@@ -1243,7 +1307,7 @@ export async function reconcileGovernanceContinuation(input) {
|
|
|
1243
1307
|
}
|
|
1244
1308
|
continue;
|
|
1245
1309
|
}
|
|
1246
|
-
const cont = await ensureGovernanceContinuation({ workspaceDir, logicalObservationKey: key, canonicalPainId: painId });
|
|
1310
|
+
const cont = await ensureGovernanceContinuation({ workspaceDir, logicalObservationKey: key, canonicalPainId: painId, conversationIngestionEnabled: input.conversationIngestionEnabled });
|
|
1247
1311
|
if (cont.ok) {
|
|
1248
1312
|
// Only count REAL actions: a task actually created, a link actually
|
|
1249
1313
|
// repaired (task existed but the marker link was missing). Healthy
|
|
@@ -1259,13 +1323,19 @@ export async function reconcileGovernanceContinuation(input) {
|
|
|
1259
1323
|
completedTails += 1;
|
|
1260
1324
|
}
|
|
1261
1325
|
else {
|
|
1326
|
+
if (cont.taskCreated)
|
|
1327
|
+
tasksEnsured += 1;
|
|
1328
|
+
if (cont.linkRepaired)
|
|
1329
|
+
linksRepaired += 1;
|
|
1262
1330
|
degradations.push(`${key}:${cont.reason}`);
|
|
1263
1331
|
}
|
|
1264
1332
|
}
|
|
1265
1333
|
// Also report stale tails scoped to governance admissions.
|
|
1266
|
-
const stale =
|
|
1267
|
-
|
|
1268
|
-
|
|
1334
|
+
const stale = input.conversationIngestionEnabled
|
|
1335
|
+
? db.prepare(`SELECT COUNT(*) AS n FROM governance_pending_promotion_tails t
|
|
1336
|
+
WHERE t.state = 'stale'
|
|
1337
|
+
AND EXISTS (SELECT 1 FROM governance_signal_admissions a WHERE a.canonical_pain_id = t.pain_ref)`).get()
|
|
1338
|
+
: { n: 0 };
|
|
1269
1339
|
const staleTails = typeof rowField(stale, 'n') === 'number' ? rowField(stale, 'n') : 0;
|
|
1270
1340
|
return {
|
|
1271
1341
|
ok: degradations.length === 0,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-principles-disciple",
|
|
3
|
-
"version": "1.145.
|
|
3
|
+
"version": "1.145.29",
|
|
4
4
|
"description": "Interactive CLI installer for Principles Disciple OpenClaw plugin",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -89,6 +89,6 @@
|
|
|
89
89
|
"directory": "packages/create-principles-disciple"
|
|
90
90
|
},
|
|
91
91
|
"pd": {
|
|
92
|
-
"bundledPluginVersion": "2.2.
|
|
92
|
+
"bundledPluginVersion": "2.2.4"
|
|
93
93
|
}
|
|
94
94
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-reconcile.d.ts","sourceRoot":"","sources":["../../src/commands/codex-reconcile.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"codex-reconcile.d.ts","sourceRoot":"","sources":["../../src/commands/codex-reconcile.ts"],"names":[],"mappings":"AAoBA,UAAU,qBAAqB;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqExF"}
|
|
@@ -15,12 +15,41 @@
|
|
|
15
15
|
* - cli-6: degraded results include reason + nextAction.
|
|
16
16
|
*/
|
|
17
17
|
import { resolveWorkspaceDir } from '../resolve-workspace.js';
|
|
18
|
-
import { reconcileGovernanceContinuation } from '@principles/host-runtime';
|
|
18
|
+
import { loadPdConfigForPlugin, reconcileGovernanceContinuation } from '@principles/host-runtime';
|
|
19
|
+
import { computeFeatureFlagsFromConfig } from '@principles/core/runtime-v2';
|
|
19
20
|
export async function handleCodexReconcile(options) {
|
|
20
21
|
const generatedAt = new Date().toISOString();
|
|
21
22
|
const workspace = resolveWorkspaceDir(options.workspace);
|
|
23
|
+
const config = loadPdConfigForPlugin(workspace);
|
|
24
|
+
if (!config.ok) {
|
|
25
|
+
const [first] = config.errors;
|
|
26
|
+
const reason = `pd_config_invalid:${first?.reason ?? 'unknown'}`;
|
|
27
|
+
const nextAction = first?.nextAction ?? 'Repair the Workspace .pd/config.yaml before reconciling; the conversation-ingestion consent could not be verified.';
|
|
28
|
+
const report = {
|
|
29
|
+
generatedAt,
|
|
30
|
+
host: 'codex',
|
|
31
|
+
workspace,
|
|
32
|
+
ok: false,
|
|
33
|
+
reason,
|
|
34
|
+
nextAction,
|
|
35
|
+
tasksEnsured: 0,
|
|
36
|
+
linksRepaired: 0,
|
|
37
|
+
pendingTails: 0,
|
|
38
|
+
completedTails: 0,
|
|
39
|
+
staleTails: 0,
|
|
40
|
+
degradations: [reason],
|
|
41
|
+
};
|
|
42
|
+
if (options.json)
|
|
43
|
+
console.log(JSON.stringify(report));
|
|
44
|
+
else
|
|
45
|
+
console.log([`Codex governance reconciliation (${workspace})`, ` reason: ${reason}`, ` next action: ${nextAction}`].join('\n'));
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const conversationIngestionEnabled = computeFeatureFlagsFromConfig(config.effective).flags.codex_conversation_ingestion?.enabled === true;
|
|
22
50
|
const result = await reconcileGovernanceContinuation({
|
|
23
51
|
workspaceDir: workspace,
|
|
52
|
+
conversationIngestionEnabled,
|
|
24
53
|
...(typeof options.limit === 'number' ? { limit: options.limit } : {}),
|
|
25
54
|
});
|
|
26
55
|
const report = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-reconcile.js","sourceRoot":"","sources":["../../src/commands/codex-reconcile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,+BAA+B,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"codex-reconcile.js","sourceRoot":"","sources":["../../src/commands/codex-reconcile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,+BAA+B,EAAE,MAAM,0BAA0B,CAAC;AAClG,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAuB5E,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,OAA8B;IACvE,MAAM,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC7C,MAAM,SAAS,GAAG,mBAAmB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACzD,MAAM,MAAM,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAChD,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;QAC9B,MAAM,MAAM,GAAG,qBAAqB,KAAK,EAAE,MAAM,IAAI,SAAS,EAAE,CAAC;QACjE,MAAM,UAAU,GAAG,KAAK,EAAE,UAAU,IAAI,oHAAoH,CAAC;QAC7J,MAAM,MAAM,GAAyB;YACnC,WAAW;YACX,IAAI,EAAE,OAAO;YACb,SAAS;YACT,EAAE,EAAE,KAAK;YACT,MAAM;YACN,UAAU;YACV,YAAY,EAAE,CAAC;YACf,aAAa,EAAE,CAAC;YAChB,YAAY,EAAE,CAAC;YACf,cAAc,EAAE,CAAC;YACjB,UAAU,EAAE,CAAC;YACb,YAAY,EAAE,CAAC,MAAM,CAAC;SACvB,CAAC;QACF,IAAI,OAAO,CAAC,IAAI;YAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;;YACjD,OAAO,CAAC,GAAG,CAAC,CAAC,oCAAoC,SAAS,GAAG,EAAE,aAAa,MAAM,EAAE,EAAE,kBAAkB,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACvI,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IACD,MAAM,4BAA4B,GAAG,6BAA6B,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,4BAA4B,EAAE,OAAO,KAAK,IAAI,CAAC;IAE1I,MAAM,MAAM,GAAG,MAAM,+BAA+B,CAAC;QACnD,YAAY,EAAE,SAAS;QACvB,4BAA4B;QAC5B,GAAG,CAAC,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACvE,CAAC,CAAC;IAEH,MAAM,MAAM,GAAyB;QACnC,WAAW;QACX,IAAI,EAAE,OAAO;QACb,SAAS;QACT,EAAE,EAAE,MAAM,CAAC,EAAE;QACb,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,YAAY,EAAE,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC;QACtC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,uBAAuB,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,8EAA8E,EAAE,CAAC;KAC5L,CAAC;IAEF,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM,CAAC,EAAE;YAAE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrC,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG;QACZ,oCAAoC,SAAS,GAAG;QAChD,yBAAyB,MAAM,CAAC,YAAY,qBAAqB,MAAM,CAAC,aAAa,GAAG;QACxF,iCAAiC,MAAM,CAAC,YAAY,kBAAkB,MAAM,CAAC,cAAc,UAAU,MAAM,CAAC,UAAU,EAAE;KACzH,CAAC;IACF,KAAK,MAAM,WAAW,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QAC9C,KAAK,CAAC,IAAI,CAAC,kBAAkB,WAAW,EAAE,CAAC,CAAC;IAC9C,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,KAAK,CAAC,IAAI,CAAC,kBAAkB,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9B,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pain-explicit.d.ts","sourceRoot":"","sources":["../../src/commands/pain-explicit.ts"],"names":[],"mappings":"AAuBA,UAAU,eAAe;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AA+BD,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC7E"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pd pain explicit — agent-assisted explicit Owner pain registration from the
|
|
3
|
+
* LIVE Codex turn context (no `PD记录痛苦:` prefix).
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* pd pain explicit --statement "<Owner 原文>" [--workspace <path>] [--json]
|
|
7
|
+
*
|
|
8
|
+
* The command takes NO lineage flags by design: the Codex hook wrote the
|
|
9
|
+
* current turn's host-verified identity to `<workspace>/.state/`, and the
|
|
10
|
+
* bridge (registerCurrentTurnOwnerStatement) re-validates it — transcript
|
|
11
|
+
* existence, CODEX_HOME containment, workspace match — inside the SAME
|
|
12
|
+
* admission chain the prefix entry uses (governance admission marker,
|
|
13
|
+
* explicit rate-limit bucket, diagnostician task ensure). The model never
|
|
14
|
+
* supplies session/rollout/turn ids, so it cannot guess or fabricate them.
|
|
15
|
+
*
|
|
16
|
+
* This is NOT `pd pain record`: that command is the unbound operator/recovery
|
|
17
|
+
* entry (manual pain ids, no admission marker); this one is the bound
|
|
18
|
+
* agent-assisted counterpart of the prefix entry.
|
|
19
|
+
*/
|
|
20
|
+
import { registerCurrentTurnOwnerStatement } from '@principles/codex-adapter';
|
|
21
|
+
import { MAX_OWNER_STATEMENT_CHARS } from '@principles/host-runtime';
|
|
22
|
+
import { resolveWorkspaceDir } from '../resolve-workspace.js';
|
|
23
|
+
const FAILURE_MESSAGES = {
|
|
24
|
+
statement_empty: 'The Owner statement is empty; nothing was recorded.',
|
|
25
|
+
statement_too_long: `The Owner statement exceeds ${MAX_OWNER_STATEMENT_CHARS} characters; nothing was recorded (never truncated).`,
|
|
26
|
+
turn_context_missing: 'No live Codex turn context: the turn already completed, the session restarted, or the PD hook did not run.',
|
|
27
|
+
turn_context_stale: 'The live Codex turn context is stale — the turn is treated as ended; nothing was recorded.',
|
|
28
|
+
turn_context_invalid: 'The Codex turn context file is malformed or unsupported; nothing was recorded.',
|
|
29
|
+
turn_context_workspace_mismatch: 'The live turn context belongs to a different PD workspace; nothing was recorded.',
|
|
30
|
+
turn_context_session_mismatch: 'The live turn context does not belong to this Codex session; nothing was recorded.',
|
|
31
|
+
caller_session_missing: 'No host-bound Codex session identity is available; nothing was recorded.',
|
|
32
|
+
caller_session_conflict: 'The host-bound Codex session and thread identities conflict; nothing was recorded.',
|
|
33
|
+
conversation_ingestion_config_invalid: 'The workspace configuration could not be validated, so conversation-ingestion consent could not be determined; nothing was recorded.',
|
|
34
|
+
transcript_unavailable: 'The Codex transcript for the live turn is unavailable; nothing was recorded.',
|
|
35
|
+
lineage_invalid: 'The live turn context failed lineage validation; nothing was recorded.',
|
|
36
|
+
rate_limited: 'The per-root-session explicit statement quota (5/hour) is exhausted; nothing was recorded.',
|
|
37
|
+
continuation_failed: 'The governance admission or diagnostician-task continuation failed; nothing was recorded.',
|
|
38
|
+
workspace_unresolvable: 'No PD workspace could be resolved for this invocation.',
|
|
39
|
+
};
|
|
40
|
+
function emitFailure(opts, failure) {
|
|
41
|
+
if (opts.json) {
|
|
42
|
+
console.log(JSON.stringify({ status: 'failed', reason: failure.reason, message: failure.message, nextAction: failure.nextAction }, null, 2));
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
console.error(`Error: ${failure.message}`);
|
|
46
|
+
console.error(`Reason: ${failure.reason}`);
|
|
47
|
+
console.error(`Next action: ${failure.nextAction}`);
|
|
48
|
+
}
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
export async function handlePainExplicit(opts) {
|
|
52
|
+
let workspaceDir;
|
|
53
|
+
try {
|
|
54
|
+
workspaceDir = resolveWorkspaceDir(opts.workspace);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
58
|
+
emitFailure(opts, { reason: 'workspace_unresolvable', message: detail.slice(0, 400), nextAction: 'Pass --workspace explicitly or run from a directory inside a PD workspace.' });
|
|
59
|
+
}
|
|
60
|
+
const result = await registerCurrentTurnOwnerStatement({
|
|
61
|
+
workspaceDir,
|
|
62
|
+
statementText: opts.statement ?? '',
|
|
63
|
+
});
|
|
64
|
+
if (result.status !== 'received') {
|
|
65
|
+
emitFailure(opts, {
|
|
66
|
+
reason: result.reason,
|
|
67
|
+
message: FAILURE_MESSAGES[result.reason] ?? 'The explicit registration was rejected; nothing was recorded.',
|
|
68
|
+
nextAction: result.nextAction,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (opts.json) {
|
|
72
|
+
console.log(JSON.stringify({
|
|
73
|
+
status: 'received',
|
|
74
|
+
disposition: result.disposition,
|
|
75
|
+
painId: result.painId,
|
|
76
|
+
taskId: result.taskId,
|
|
77
|
+
logicalObservationKey: result.logicalObservationKey,
|
|
78
|
+
...(result.promotionDegraded !== undefined ? { promotionDegraded: result.promotionDegraded } : {}),
|
|
79
|
+
}, null, 2));
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
const duplicateNote = result.disposition === 'already_admitted' ? ' (duplicate submission merged into the same signal)' : '';
|
|
83
|
+
console.log(`Pain registered${duplicateNote}: painId=${result.painId} taskId=${result.taskId}`);
|
|
84
|
+
if (result.promotionDegraded !== undefined) {
|
|
85
|
+
console.error(`Warning: session-evidence promotion degraded: ${result.promotionDegraded}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=pain-explicit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pain-explicit.js","sourceRoot":"","sources":["../../src/commands/pain-explicit.ts"],"names":[],"mappings":"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"}
|
package/pd-cli/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { Command, Option } from 'commander';
|
|
9
9
|
import { handlePainRecord } from './commands/pain-record.js';
|
|
10
|
+
import { handlePainExplicit } from './commands/pain-explicit.js';
|
|
10
11
|
import { registerPainRetryCommand } from './commands/pain-retry.js';
|
|
11
12
|
import { handlePainRecall, parsePainRecallLimit } from './commands/pain-recall.js';
|
|
12
13
|
import { handlePainEvidence } from './commands/pain-evidence.js';
|
|
@@ -121,6 +122,15 @@ painCmd
|
|
|
121
122
|
.action(async (opts) => {
|
|
122
123
|
await handlePainRecord(opts);
|
|
123
124
|
});
|
|
125
|
+
painCmd
|
|
126
|
+
.command('explicit')
|
|
127
|
+
.description('Register an explicit Owner pain statement from the live Codex turn context (agent-assisted, no prefix; same admission chain as PD记录痛苦: — takes no lineage flags)')
|
|
128
|
+
.option('--statement <text>', 'Owner verbatim statement (1-500 chars, required; rejected when longer — never truncated)')
|
|
129
|
+
.option('-w, --workspace <path>', 'Workspace directory')
|
|
130
|
+
.option('--json', 'Output raw JSON')
|
|
131
|
+
.action(async (opts) => {
|
|
132
|
+
await handlePainExplicit(opts);
|
|
133
|
+
});
|
|
124
134
|
registerPainRetryCommand(painCmd);
|
|
125
135
|
painCmd
|
|
126
136
|
.command('list')
|