create-principles-disciple 1.144.20 → 1.144.22
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/codex-evidence-recorder.d.ts +56 -0
- package/codex-adapter/dist/codex-evidence-recorder.js +235 -0
- package/codex-adapter/dist/pd-hook.d.ts +6 -0
- package/codex-adapter/dist/pd-hook.js +96 -13
- package/codex-adapter/package.json +1 -1
- package/console/dist/server/index.js +18 -0
- package/console/dist/server/models/ActivationsConsoleModel.d.ts +13 -1
- package/console/dist/server/models/ActivationsConsoleModel.js +54 -2
- package/console/dist/server/models/ApprovalsConsoleModel.d.ts +98 -19
- package/console/dist/server/models/ApprovalsConsoleModel.js +338 -29
- package/console/dist/server/models/ApprovalsGroupedConsoleModel.d.ts +17 -7
- package/console/dist/server/models/ApprovalsGroupedConsoleModel.js +134 -54
- package/console/dist/server/models/ReceiptsConsoleModel.d.ts +44 -1
- package/console/dist/server/models/ReceiptsConsoleModel.js +102 -0
- package/console/dist/server/routes/activations.js +96 -1
- package/console/dist/server/routes/approvals-grouped.js +15 -2
- package/console/dist/server/routes/approvals.js +40 -2
- package/console/dist/server/routes/evidence-outcomes.d.ts +29 -0
- package/console/dist/server/routes/evidence-outcomes.js +137 -0
- package/console/dist/server/routes/receipts.js +56 -0
- package/console/dist/shared/prompt-injection-contract.d.ts +71 -0
- package/console/dist/ui/api.d.ts +94 -4
- package/console/dist/ui/api.js +277 -8
- package/console/dist/ui/i18n/en.json +113 -13
- package/console/dist/ui/i18n/zh-CN.json +113 -13
- package/console/dist/ui/pages/activation/ActivationPage.js +80 -2
- package/console/dist/ui/pages/focus/FocusPage.js +49 -21
- package/console/dist/ui/pages/principles/PrincipleDetailPage.js +68 -3
- package/console/dist/ui/utils/approval-warning-localization.js +39 -0
- package/console/dist/ui/utils/validators.d.ts +10 -1
- package/console/dist/ui/utils/validators.js +126 -0
- package/console/dist/web/assets/app.css +15 -0
- package/console/dist/web/assets/app.js +29 -28
- package/core/dist/runtime-v2/activation/activation-dispatcher.d.ts +4 -0
- package/core/dist/runtime-v2/activation/activation-dispatcher.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/activation-dispatcher.js +215 -63
- package/core/dist/runtime-v2/activation/activation-dispatcher.js.map +1 -1
- package/core/dist/runtime-v2/activation/activation-types.d.ts +48 -0
- package/core/dist/runtime-v2/activation/activation-types.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/activation-types.js.map +1 -1
- package/core/dist/runtime-v2/activation/approval-completion-service.d.ts +9 -0
- package/core/dist/runtime-v2/activation/approval-completion-service.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/approval-completion-service.js +4 -1
- package/core/dist/runtime-v2/activation/approval-completion-service.js.map +1 -1
- package/core/dist/runtime-v2/activation/index.d.ts +4 -2
- package/core/dist/runtime-v2/activation/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/index.js +4 -1
- package/core/dist/runtime-v2/activation/index.js.map +1 -1
- package/core/dist/runtime-v2/activation/low-risk-writers.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/low-risk-writers.js +2 -1
- package/core/dist/runtime-v2/activation/low-risk-writers.js.map +1 -1
- package/core/dist/runtime-v2/activation/prompt-activation-reader-contract.d.ts +11 -0
- package/core/dist/runtime-v2/activation/prompt-activation-reader-contract.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/prompt-activation-reader-contract.js +12 -2
- package/core/dist/runtime-v2/activation/prompt-activation-reader-contract.js.map +1 -1
- package/core/dist/runtime-v2/activation/prompt-replacement.d.ts +79 -0
- package/core/dist/runtime-v2/activation/prompt-replacement.d.ts.map +1 -0
- package/core/dist/runtime-v2/activation/prompt-replacement.js +171 -0
- package/core/dist/runtime-v2/activation/prompt-replacement.js.map +1 -0
- package/core/dist/runtime-v2/activation/sqlite-activation-state-store.d.ts +17 -1
- package/core/dist/runtime-v2/activation/sqlite-activation-state-store.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/sqlite-activation-state-store.js +132 -0
- package/core/dist/runtime-v2/activation/sqlite-activation-state-store.js.map +1 -1
- package/core/dist/runtime-v2/index.d.ts +8 -2
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +8 -2
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-prompt-builder.d.ts +13 -1
- package/core/dist/runtime-v2/internalization/scribe-prompt-builder.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-prompt-builder.js +15 -2
- package/core/dist/runtime-v2/internalization/scribe-prompt-builder.js.map +1 -1
- package/core/dist/runtime-v2/intervention-evidence-normalizer.d.ts +28 -0
- package/core/dist/runtime-v2/intervention-evidence-normalizer.d.ts.map +1 -0
- package/core/dist/runtime-v2/intervention-evidence-normalizer.js +654 -0
- package/core/dist/runtime-v2/intervention-evidence-normalizer.js.map +1 -0
- package/core/dist/runtime-v2/receipt-coverage.d.ts +8 -0
- package/core/dist/runtime-v2/receipt-coverage.d.ts.map +1 -1
- package/core/dist/runtime-v2/receipt-coverage.js +18 -0
- package/core/dist/runtime-v2/receipt-coverage.js.map +1 -1
- package/core/dist/runtime-v2/store/intervention-evidence-schema.d.ts +6 -0
- package/core/dist/runtime-v2/store/intervention-evidence-schema.d.ts.map +1 -0
- package/core/dist/runtime-v2/store/intervention-evidence-schema.js +122 -0
- package/core/dist/runtime-v2/store/intervention-evidence-schema.js.map +1 -0
- package/core/dist/runtime-v2/store/sqlite-connection.d.ts +8 -0
- package/core/dist/runtime-v2/store/sqlite-connection.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/sqlite-connection.js +62 -3
- package/core/dist/runtime-v2/store/sqlite-connection.js.map +1 -1
- package/core/dist/runtime-v2/store/sqlite-intervention-evidence-store.d.ts +67 -0
- package/core/dist/runtime-v2/store/sqlite-intervention-evidence-store.d.ts.map +1 -0
- package/core/dist/runtime-v2/store/sqlite-intervention-evidence-store.js +737 -0
- package/core/dist/runtime-v2/store/sqlite-intervention-evidence-store.js.map +1 -0
- package/core/dist/runtime-v2/types/event-types.d.ts +6 -0
- package/core/dist/runtime-v2/types/event-types.d.ts.map +1 -1
- package/core/dist/runtime-v2/types/event-types.js +2 -0
- package/core/dist/runtime-v2/types/event-types.js.map +1 -1
- package/core/dist/runtime-v2/types/intervention-evidence-contract.d.ts +287 -0
- package/core/dist/runtime-v2/types/intervention-evidence-contract.d.ts.map +1 -0
- package/core/dist/runtime-v2/types/intervention-evidence-contract.js +84 -0
- package/core/dist/runtime-v2/types/intervention-evidence-contract.js.map +1 -0
- package/core/package.json +5 -1
- package/host-runtime/dist/active-principle-prompt.d.ts +2 -0
- package/host-runtime/dist/active-principle-prompt.js +1 -1
- package/host-runtime/dist/host-liveness-contract.d.ts +27 -0
- package/host-runtime/dist/host-liveness-contract.js +38 -12
- package/host-runtime/dist/index.d.ts +4 -0
- package/host-runtime/dist/index.js +16 -0
- package/host-runtime/dist/intervention-evidence-ingress.d.ts +41 -0
- package/host-runtime/dist/intervention-evidence-ingress.js +272 -0
- package/host-runtime/dist/prompt-activation-deliverability.d.ts +106 -0
- package/host-runtime/dist/prompt-activation-deliverability.js +176 -0
- package/host-runtime/dist/prompt-injection-event-evidence.d.ts +51 -0
- package/host-runtime/dist/prompt-injection-event-evidence.js +141 -0
- package/host-runtime/dist/prompt-injection-projection.d.ts +135 -19
- package/host-runtime/dist/prompt-injection-projection.js +224 -20
- package/host-runtime/dist/prompt-injection-status.d.ts +44 -0
- package/host-runtime/dist/prompt-injection-status.js +147 -0
- package/host-runtime/package.json +1 -1
- package/package.json +2 -2
- package/pd-cli/dist/commands/runtime-activation.d.ts +4 -0
- package/pd-cli/dist/commands/runtime-activation.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-activation.js +142 -23
- package/pd-cli/dist/commands/runtime-activation.js.map +1 -1
- package/plugin/dist/bundle.js +733 -568
- package/plugin/dist/core/intervention-evidence-recorder.d.ts +65 -0
- package/plugin/dist/governance-audit.js +2 -2
- package/plugin/dist/hooks/gate.d.ts +3 -0
- package/plugin/dist/openclaw.plugin.json +1 -1
- package/plugin/openclaw.plugin.json +1 -1
- package/plugin/package.json +1 -1
- package/release-manager/package.json +2 -2
|
@@ -1,11 +1,42 @@
|
|
|
1
1
|
import type { ApprovalListFilter, ApprovalListResult, ApprovalDecisionResult, ApprovalRecord, ApprovalStatus } from '@principles/core/runtime-v2';
|
|
2
2
|
import type { ApprovalWithContext, ActivationDecision } from '@principles/core/runtime-v2';
|
|
3
|
+
import { type PromptInjectionTargetHost } from '@principles/host-runtime';
|
|
3
4
|
type UnsupportedChannelResult = {
|
|
4
5
|
ok: false;
|
|
5
6
|
error: 'unsupported_channel';
|
|
6
7
|
channel: string;
|
|
7
8
|
};
|
|
8
9
|
type ChannelGuardedDecisionResult = ApprovalDecisionResult | UnsupportedChannelResult;
|
|
10
|
+
/** R-B3 propose-revision result: reviewable diff + queued approval, or a structured refusal. */
|
|
11
|
+
export type ProposePromptRevisionResult = {
|
|
12
|
+
ok: true;
|
|
13
|
+
/** true = an equivalent pending revision approval already existed (idempotent replay). */
|
|
14
|
+
alreadyPending: boolean;
|
|
15
|
+
newArtifactId: string;
|
|
16
|
+
approvalId: string;
|
|
17
|
+
supersededActivationId: string;
|
|
18
|
+
oldStatement: string;
|
|
19
|
+
newStatement: string;
|
|
20
|
+
title: string;
|
|
21
|
+
diff: {
|
|
22
|
+
intentContract?: unknown;
|
|
23
|
+
note?: string;
|
|
24
|
+
};
|
|
25
|
+
deliverability?: {
|
|
26
|
+
status: string;
|
|
27
|
+
reason?: string;
|
|
28
|
+
costChars?: number;
|
|
29
|
+
budget?: number;
|
|
30
|
+
route?: string;
|
|
31
|
+
};
|
|
32
|
+
} | {
|
|
33
|
+
ok: false;
|
|
34
|
+
error: 'not_found' | 'activation_not_live_prompt' | 'artifact_unavailable' | 'revision_validation_failed' | 'revision_enqueue_failed';
|
|
35
|
+
reason?: string;
|
|
36
|
+
nextAction?: string;
|
|
37
|
+
activationId?: string;
|
|
38
|
+
artifactId?: string;
|
|
39
|
+
};
|
|
9
40
|
export type ApproveWithActivationResult = {
|
|
10
41
|
ok: true;
|
|
11
42
|
record: ApprovalRecord;
|
|
@@ -27,6 +58,36 @@ export type ApproveWithActivationResult = {
|
|
|
27
58
|
error: 'activation_failed';
|
|
28
59
|
reason: string;
|
|
29
60
|
approvalRolledBack: boolean;
|
|
61
|
+
nextAction?: string;
|
|
62
|
+
} | {
|
|
63
|
+
ok: false;
|
|
64
|
+
error: 'prompt_replacement_refused';
|
|
65
|
+
reason: string;
|
|
66
|
+
nextAction: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* PD_PROMPT_CAPACITY_V1 R-B2: the prompt-channel write gate refused BEFORE
|
|
70
|
+
* any governance write. The approval stays pending, the artifact stays
|
|
71
|
+
* untouched, no live activation and no ledger upgrade happens.
|
|
72
|
+
*/
|
|
73
|
+
| {
|
|
74
|
+
ok: false;
|
|
75
|
+
error: 'prompt_capacity_refused';
|
|
76
|
+
reason: string;
|
|
77
|
+
nextAction: string;
|
|
78
|
+
capacity: {
|
|
79
|
+
category: 'single_item_exceeds_budget' | 'route_unconfirmed' | 'content_unconfirmed';
|
|
80
|
+
route?: 'legacy_trim' | 'shared_render';
|
|
81
|
+
hostKind?: 'openclaw' | 'codex';
|
|
82
|
+
budget?: number;
|
|
83
|
+
costChars?: number;
|
|
84
|
+
overByChars?: number;
|
|
85
|
+
perHost?: {
|
|
86
|
+
hostKind: 'openclaw' | 'codex';
|
|
87
|
+
fits: boolean;
|
|
88
|
+
costChars: number;
|
|
89
|
+
}[];
|
|
90
|
+
};
|
|
30
91
|
};
|
|
31
92
|
export type ReopenApprovalResult = {
|
|
32
93
|
ok: true;
|
|
@@ -53,28 +114,31 @@ export declare class ApprovalsConsoleModel {
|
|
|
53
114
|
getApprovalDetail(approvalId: string): Promise<(ApprovalWithContext & {
|
|
54
115
|
isMvpProven: boolean;
|
|
55
116
|
}) | null>;
|
|
56
|
-
approve(approvalId: string, decidedBy: string,
|
|
117
|
+
approve(approvalId: string, decidedBy: string, options?: {
|
|
118
|
+
note?: string;
|
|
119
|
+
targetHost?: PromptInjectionTargetHost;
|
|
120
|
+
intentReviewed?: boolean;
|
|
121
|
+
reviewedArtifactId?: string;
|
|
122
|
+
retryActivation?: boolean;
|
|
123
|
+
}): Promise<ApproveWithActivationResult>;
|
|
124
|
+
/**
|
|
125
|
+
* PD_PROMPT_CAPACITY_V1 R-B2: run the shared host-runtime deliverability
|
|
126
|
+
* precheck for a prompt-channel approval artifact. Returns the refusal
|
|
127
|
+
* result when the single item cannot be delivered or the facts cannot be
|
|
128
|
+
* confirmed; returns undefined when the write may proceed.
|
|
129
|
+
*/
|
|
130
|
+
private precheckPromptCapacity;
|
|
57
131
|
/**
|
|
58
132
|
* PRI-890 (PRI-768 v6-02): recompute the production prompt injection
|
|
59
133
|
* projection and report whether `activationId` made it into the injected
|
|
60
|
-
* set.
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* PR-1894 — the console holds no session, so it cannot supply the production
|
|
70
|
-
* round key and the projection runs `legacy_fifo_prefix_v1`. That is NOT a
|
|
71
|
-
* conservative forecast: FIFO structurally drops the newest activation from
|
|
72
|
-
* every truncated selection, while the live plugin route rotates and injects
|
|
73
|
-
* it within N user turns (PRI-904). Reporting that as "will NOT enter agent
|
|
74
|
-
* behavior until older ones are deactivated" told the Owner a falsehood and
|
|
75
|
-
* pushed them to deactivate healthy principles. The console now reports the
|
|
76
|
-
* policy it actually ran and, when the activation is merely rotated out of
|
|
77
|
-
* the current window, says so instead of claiming starvation.
|
|
134
|
+
* set. PD_PROMPT_CAPACITY_V1: the projection follows the host-fact-bound
|
|
135
|
+
* route (resolveLivePromptInjectionProjection), an oversized activation is
|
|
136
|
+
* explained as OVERSIZED rather than folded into generic budget exclusion
|
|
137
|
+
* (AC-02), and an unconfirmed route yields an explicit
|
|
138
|
+
* `injection_capacity_unconfirmed` warning instead of a guessed FIFO or
|
|
139
|
+
* rotation story (AC-01). Budget exclusion remains a warning, never a
|
|
140
|
+
* failure — the activation is committed and the Owner decides whether to
|
|
141
|
+
* retire older principles.
|
|
78
142
|
*/
|
|
79
143
|
private checkPromptInjectionBudget;
|
|
80
144
|
/**
|
|
@@ -141,6 +205,21 @@ export declare class ApprovalsConsoleModel {
|
|
|
141
205
|
private readSafeGetById;
|
|
142
206
|
/** Returns null when the approvals table does not exist. */
|
|
143
207
|
private readSafeList;
|
|
208
|
+
/**
|
|
209
|
+
* PD_PROMPT_CAPACITY_V1 R-B3: Owner-initiated "modify to injectable version"
|
|
210
|
+
* entry on a LIVE prompt activation. Produces a NEW artifact version (the
|
|
211
|
+
* old approved artifact stays immutable), re-validates through the real
|
|
212
|
+
* scribe content contract, enqueues a NORMAL pending approval for it (the
|
|
213
|
+
* Owner reviews the diff + intent fields on the focus page and approves —
|
|
214
|
+
* the approve path then performs the atomic replacement). Duplicate requests
|
|
215
|
+
* are idempotent: an existing pending revision approval is returned as-is.
|
|
216
|
+
*/
|
|
217
|
+
proposePromptRevision(input: {
|
|
218
|
+
activationId: string;
|
|
219
|
+
statement: string;
|
|
220
|
+
editedBy: string;
|
|
221
|
+
targetHost?: PromptInjectionTargetHost;
|
|
222
|
+
}): Promise<ProposePromptRevisionResult>;
|
|
144
223
|
dispose(): void;
|
|
145
224
|
}
|
|
146
225
|
export {};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
|
-
import { SqliteConnection, SqliteApprovalQueueStore, SqliteActivationStateStore, SqlitePIArtifactStore, ActivationDispatcher, PromptWriter, DeferArchiveWriter, RuleHostWriter, createProductionGateDeps, ApprovalCompletionService, ApprovalQueue, mapConfidenceToLabel, isArtifactRevisionOf, PrincipleTreeLedgerAdapter, MVP_CHANNELS, } from '@principles/core/runtime-v2';
|
|
3
|
+
import { SqliteConnection, SqliteApprovalQueueStore, SqliteActivationStateStore, SqlitePIArtifactStore, ActivationDispatcher, PromptWriter, DeferArchiveWriter, RuleHostWriter, createProductionGateDeps, ApprovalCompletionService, ApprovalQueue, mapConfidenceToLabel, isArtifactRevisionOf, detectPromptReplacementTarget, buildOwnerRevisionArtifact, PrincipleTreeLedgerAdapter, MVP_CHANNELS, } from '@principles/core/runtime-v2';
|
|
4
4
|
import { resolveLedgerPrincipleId } from './principle-id-resolution.js';
|
|
5
5
|
import { loadPdConfig, computeFlagsFromLoadResult } from '../config/pd-config-store.js';
|
|
6
|
-
import { resolveWorkspaceHostToolSemantics,
|
|
6
|
+
import { resolveWorkspaceHostToolSemantics, checkPromptArtifactDeliverabilityById, resolveLivePromptInjectionProjection, } from '@principles/host-runtime';
|
|
7
7
|
const MVP_PROVEN_CHANNELS = new Set(MVP_CHANNELS);
|
|
8
8
|
const EMPTY_STATS = { pending: 0, approved: 0, rejected: 0, cancelled: 0 };
|
|
9
9
|
function isMissingTableError(err) {
|
|
@@ -75,7 +75,9 @@ export class ApprovalsConsoleModel {
|
|
|
75
75
|
isMvpProven: MVP_PROVEN_CHANNELS.has(record.channel),
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
|
-
async approve(approvalId, decidedBy,
|
|
78
|
+
async approve(approvalId, decidedBy, options) {
|
|
79
|
+
const note = options?.note;
|
|
80
|
+
const targetHost = options?.targetHost;
|
|
79
81
|
if (!stateDbExists(this.workspaceDir)) {
|
|
80
82
|
return { ok: false, error: 'not_found' };
|
|
81
83
|
}
|
|
@@ -85,10 +87,58 @@ export class ApprovalsConsoleModel {
|
|
|
85
87
|
if (!MVP_PROVEN_CHANNELS.has(existing.channel)) {
|
|
86
88
|
return { ok: false, error: 'unsupported_channel', channel: existing.channel };
|
|
87
89
|
}
|
|
90
|
+
// PD_PROMPT_CAPACITY_V1 R-B2/R-B3: for a PENDING prompt approval —
|
|
91
|
+
// (1) the route-aware single-artifact capacity precheck BEFORE any
|
|
92
|
+
// governance write (re-reads the CURRENT artifact and route/config at
|
|
93
|
+
// submit time — a stale preview never authorizes the write, AC-08;
|
|
94
|
+
// refusal keeps the approval pending, the artifact, no activation, no
|
|
95
|
+
// ledger upgrade), then (2) replacement detection: approving an artifact
|
|
96
|
+
// that is a REVISION of a live prompt activation means version
|
|
97
|
+
// replacement — pass the single supersede target through so the
|
|
98
|
+
// activation commit replaces the old version atomically (detection fails
|
|
99
|
+
// closed on ambiguity). Already-decided rows reach their terminal-state
|
|
100
|
+
// error (409) untouched by both gates.
|
|
101
|
+
let supersedeActivationId;
|
|
102
|
+
let supersedeArtifactId;
|
|
103
|
+
if (existing.channel === 'prompt' && (existing.status === 'pending' || (existing.status === 'approved' && options?.retryActivation === true))) {
|
|
104
|
+
const precheck = await this.precheckPromptCapacity(existing.artifactId, targetHost);
|
|
105
|
+
if (precheck !== undefined)
|
|
106
|
+
return precheck;
|
|
107
|
+
const { connection } = this.createReadContext();
|
|
108
|
+
try {
|
|
109
|
+
const piArtifactStore = new SqlitePIArtifactStore(connection);
|
|
110
|
+
const detection = await detectPromptReplacementTarget({
|
|
111
|
+
approvalArtifactId: existing.artifactId,
|
|
112
|
+
getArtifactById: async (id) => piArtifactStore.getArtifactById(id),
|
|
113
|
+
listPromptActivations: async () => new SqliteActivationStateStore(connection).listPromptActivations(),
|
|
114
|
+
});
|
|
115
|
+
if (!detection.ok) {
|
|
116
|
+
return {
|
|
117
|
+
ok: false,
|
|
118
|
+
error: 'prompt_replacement_refused',
|
|
119
|
+
reason: detection.error,
|
|
120
|
+
nextAction: detection.nextAction,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
supersedeActivationId = detection.target?.supersededActivationId;
|
|
124
|
+
supersedeArtifactId = detection.target?.supersededArtifactId;
|
|
125
|
+
if (existing.status === 'pending' && detection.target !== null && (options?.intentReviewed !== true || options.reviewedArtifactId !== existing.artifactId)) {
|
|
126
|
+
return { ok: false, error: 'prompt_replacement_refused', reason: 'revision_intent_review_required', nextAction: 'review the old and new statements and retained intent fields, then confirm intent review on the existing approval action' };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
try {
|
|
131
|
+
connection.close();
|
|
132
|
+
}
|
|
133
|
+
catch { /* best-effort */ }
|
|
134
|
+
}
|
|
135
|
+
}
|
|
88
136
|
const { queue: writeQueue, connection: writeConnection } = this.createWriteContext();
|
|
89
137
|
let approvalResult;
|
|
90
138
|
try {
|
|
91
|
-
approvalResult =
|
|
139
|
+
approvalResult = existing.channel === 'prompt' && existing.status === 'approved' && options?.retryActivation === true
|
|
140
|
+
? { ok: true, record: existing }
|
|
141
|
+
: await writeQueue.approve(approvalId, decidedBy, note);
|
|
92
142
|
}
|
|
93
143
|
finally {
|
|
94
144
|
try {
|
|
@@ -102,11 +152,15 @@ export class ApprovalsConsoleModel {
|
|
|
102
152
|
}
|
|
103
153
|
return { ok: false, error: 'not_found' };
|
|
104
154
|
}
|
|
105
|
-
const activation = await this.dispatchActivationAfterApproval(
|
|
106
|
-
//
|
|
155
|
+
const activation = await this.dispatchActivationAfterApproval(approvalResult.record, approvalResult.record.decidedBy ?? 'unknown', { supersedeActivationId, supersedeArtifactId });
|
|
156
|
+
// Prompt failures retain the Owner decision; other channels preserve their existing rollback contract.
|
|
107
157
|
if (activation && !isActivationSuccess(activation)) {
|
|
108
158
|
// eslint-disable-next-line no-restricted-syntax -- 'in' required for discriminated union narrowing (ActivationDecision)
|
|
109
159
|
const detail = 'reason' in activation ? activation.reason : activation.decision;
|
|
160
|
+
if (existing.channel === 'prompt') {
|
|
161
|
+
return { ok: false, error: 'activation_failed', reason: detail, approvalRolledBack: false,
|
|
162
|
+
nextAction: `Approval remains approved. Retry with pd runtime activation approve --approval-id ${approvalId} --retry-activation${targetHost === undefined ? '' : ` --target-host ${targetHost}`}.` };
|
|
163
|
+
}
|
|
110
164
|
let approvalRolledBack = false;
|
|
111
165
|
try {
|
|
112
166
|
const { queue: rollbackQueue, connection: rollbackConnection } = this.createWriteContext();
|
|
@@ -164,47 +218,108 @@ export class ApprovalsConsoleModel {
|
|
|
164
218
|
// isActivationSuccess is not a type predicate; the success variants all
|
|
165
219
|
// carry activationId (activation-types.ts) — narrow with `in` (rc-2).
|
|
166
220
|
if ('activationId' in activation) {
|
|
167
|
-
injectionWarning = await this.checkPromptInjectionBudget(activation.activationId);
|
|
221
|
+
injectionWarning = await this.checkPromptInjectionBudget(activation.activationId, targetHost);
|
|
168
222
|
}
|
|
169
223
|
}
|
|
170
224
|
const warnings = [ledgerWarning, injectionWarning].filter((w) => w !== undefined);
|
|
171
225
|
return { ok: true, record: approvalResult.record, activation, warning: warnings.length > 0 ? warnings.join('; ') : undefined };
|
|
172
226
|
}
|
|
227
|
+
/**
|
|
228
|
+
* PD_PROMPT_CAPACITY_V1 R-B2: run the shared host-runtime deliverability
|
|
229
|
+
* precheck for a prompt-channel approval artifact. Returns the refusal
|
|
230
|
+
* result when the single item cannot be delivered or the facts cannot be
|
|
231
|
+
* confirmed; returns undefined when the write may proceed.
|
|
232
|
+
*/
|
|
233
|
+
async precheckPromptCapacity(artifactId, targetHost) {
|
|
234
|
+
let deliverability;
|
|
235
|
+
try {
|
|
236
|
+
deliverability = await checkPromptArtifactDeliverabilityById({
|
|
237
|
+
workspaceDir: this.workspaceDir,
|
|
238
|
+
artifactId,
|
|
239
|
+
targetHost,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
// rc-9: a crashed precheck is NOT a pass — refuse with the reason.
|
|
244
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
245
|
+
return {
|
|
246
|
+
ok: false,
|
|
247
|
+
error: 'prompt_capacity_refused',
|
|
248
|
+
reason: `prompt_capacity_precheck_failed: ${message}`,
|
|
249
|
+
nextAction: 'check the workspace .pd/state.db readability and retry the approval',
|
|
250
|
+
capacity: { category: 'content_unconfirmed' },
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
if (deliverability.status === 'deliverable')
|
|
254
|
+
return undefined;
|
|
255
|
+
if (deliverability.status === 'undeliverable') {
|
|
256
|
+
return {
|
|
257
|
+
ok: false,
|
|
258
|
+
error: 'prompt_capacity_refused',
|
|
259
|
+
reason: `single_item_exceeds_budget: this principle's own serialized entry is ${deliverability.costChars} chars > the ${deliverability.budget}-char budget on the ${deliverability.route === 'shared_render' ? 'shared full-render' : 'list-entry'} route${deliverability.hostKind !== undefined ? ` (host ${deliverability.hostKind})` : ''} — approving it could never inject it`,
|
|
260
|
+
nextAction: deliverability.nextAction,
|
|
261
|
+
capacity: {
|
|
262
|
+
category: 'single_item_exceeds_budget',
|
|
263
|
+
route: deliverability.route,
|
|
264
|
+
...(deliverability.hostKind !== undefined ? { hostKind: deliverability.hostKind } : {}),
|
|
265
|
+
budget: deliverability.budget,
|
|
266
|
+
costChars: deliverability.costChars,
|
|
267
|
+
overByChars: deliverability.overByChars,
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
ok: false,
|
|
273
|
+
error: 'prompt_capacity_refused',
|
|
274
|
+
reason: deliverability.reason,
|
|
275
|
+
nextAction: deliverability.nextAction,
|
|
276
|
+
capacity: {
|
|
277
|
+
category: deliverability.reason.startsWith('route_unconfirmed') ? 'route_unconfirmed' : 'content_unconfirmed',
|
|
278
|
+
...(deliverability.perHost !== undefined
|
|
279
|
+
? { perHost: deliverability.perHost.map((entry) => ({ hostKind: entry.hostKind, fits: entry.fits, costChars: entry.costChars })) }
|
|
280
|
+
: {}),
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
}
|
|
173
284
|
/**
|
|
174
285
|
* PRI-890 (PRI-768 v6-02): recompute the production prompt injection
|
|
175
286
|
* projection and report whether `activationId` made it into the injected
|
|
176
|
-
* set.
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
* PR-1894 — the console holds no session, so it cannot supply the production
|
|
186
|
-
* round key and the projection runs `legacy_fifo_prefix_v1`. That is NOT a
|
|
187
|
-
* conservative forecast: FIFO structurally drops the newest activation from
|
|
188
|
-
* every truncated selection, while the live plugin route rotates and injects
|
|
189
|
-
* it within N user turns (PRI-904). Reporting that as "will NOT enter agent
|
|
190
|
-
* behavior until older ones are deactivated" told the Owner a falsehood and
|
|
191
|
-
* pushed them to deactivate healthy principles. The console now reports the
|
|
192
|
-
* policy it actually ran and, when the activation is merely rotated out of
|
|
193
|
-
* the current window, says so instead of claiming starvation.
|
|
287
|
+
* set. PD_PROMPT_CAPACITY_V1: the projection follows the host-fact-bound
|
|
288
|
+
* route (resolveLivePromptInjectionProjection), an oversized activation is
|
|
289
|
+
* explained as OVERSIZED rather than folded into generic budget exclusion
|
|
290
|
+
* (AC-02), and an unconfirmed route yields an explicit
|
|
291
|
+
* `injection_capacity_unconfirmed` warning instead of a guessed FIFO or
|
|
292
|
+
* rotation story (AC-01). Budget exclusion remains a warning, never a
|
|
293
|
+
* failure — the activation is committed and the Owner decides whether to
|
|
294
|
+
* retire older principles.
|
|
194
295
|
*/
|
|
195
|
-
async checkPromptInjectionBudget(activationId) {
|
|
196
|
-
let
|
|
296
|
+
async checkPromptInjectionBudget(activationId, targetHost) {
|
|
297
|
+
let resolution;
|
|
197
298
|
try {
|
|
198
|
-
|
|
299
|
+
resolution = await resolveLivePromptInjectionProjection({
|
|
300
|
+
workspaceDir: this.workspaceDir,
|
|
301
|
+
targetHost,
|
|
302
|
+
});
|
|
199
303
|
}
|
|
200
304
|
catch (error) {
|
|
201
305
|
const message = error instanceof Error ? error.message : String(error);
|
|
202
306
|
return `injection_budget_check_failed: could not recompute the prompt injection projection (${message}); the activation is committed but its injection status is unverified. nextAction=check .pd/state.db readability and re-run pd runtime activation list`;
|
|
203
307
|
}
|
|
308
|
+
if (resolution.status === 'unconfirmed') {
|
|
309
|
+
return `injection_capacity_unconfirmed: the activation is committed but this workspace's effective injection route cannot be confirmed (${resolution.decision.unconfirmedReason ?? 'unknown reason'}); per-host forecasts: ${resolution.perHost.map((entry) => `${entry.hostKind}=${entry.projection.route} (${entry.projection.usedChars}c${entry.projection.truncated ? ', truncated' : ''})`).join('; ')}. nextAction=${resolution.decision.nextAction ?? 'pass an explicit target host (openclaw|codex)'}`;
|
|
310
|
+
}
|
|
311
|
+
const { projection } = resolution;
|
|
204
312
|
if (projection.injectedActivationIds.includes(activationId)) {
|
|
205
313
|
return undefined;
|
|
206
314
|
}
|
|
207
315
|
const projectionDetail = projection.warnings.length > 0 ? ` projection warnings: ${projection.warnings.join(' | ')}` : '';
|
|
316
|
+
// AC-02/R-A3: oversized is independent of `truncated` — say WHY first.
|
|
317
|
+
if (projection.oversizedActivationIds.includes(activationId)) {
|
|
318
|
+
const scopeNote = projection.route === 'shared_render'
|
|
319
|
+
? 'the full directive render of this single principle exceeds the cap'
|
|
320
|
+
: 'its own list entry exceeds the cap';
|
|
321
|
+
return `injection_oversized: the activation is committed but ${scopeNote} (${projection.budget} chars, measured in UTF-16 units on the ${projection.route} route) — it can never enter the prompt as written, regardless of rotation or free capacity. nextAction=shorten the statement via the activations page "modify to injectable version" entry, or deactivate this activation`;
|
|
322
|
+
}
|
|
208
323
|
if (!projection.truncated) {
|
|
209
324
|
// Not included and the budget did NOT truncate — the projection skipped
|
|
210
325
|
// this activation for another reason (e.g. artifact resolution). Report
|
|
@@ -378,7 +493,7 @@ export class ApprovalsConsoleModel {
|
|
|
378
493
|
catch { /* best-effort */ }
|
|
379
494
|
}
|
|
380
495
|
}
|
|
381
|
-
async dispatchActivationAfterApproval(existing, decidedBy) {
|
|
496
|
+
async dispatchActivationAfterApproval(existing, decidedBy, replacement) {
|
|
382
497
|
// Feature flag gate (Contract F): when story_a_approval_completion is disabled,
|
|
383
498
|
// the new orchestrator is deactivated without damaging existing data.
|
|
384
499
|
// The approval remains in 'approved' status; only activation is skipped.
|
|
@@ -482,6 +597,7 @@ export class ApprovalsConsoleModel {
|
|
|
482
597
|
approvalId: existing.approvalId,
|
|
483
598
|
actor: { kind: 'human', userId: decidedBy },
|
|
484
599
|
now: new Date().toISOString(),
|
|
600
|
+
...(replacement?.supersedeActivationId !== undefined ? { ...replacement, supersedeDecidedBy: decidedBy } : {}),
|
|
485
601
|
});
|
|
486
602
|
if (!completionResult.ok) {
|
|
487
603
|
return {
|
|
@@ -617,6 +733,199 @@ export class ApprovalsConsoleModel {
|
|
|
617
733
|
catch { /* best-effort */ }
|
|
618
734
|
}
|
|
619
735
|
}
|
|
736
|
+
/**
|
|
737
|
+
* PD_PROMPT_CAPACITY_V1 R-B3: Owner-initiated "modify to injectable version"
|
|
738
|
+
* entry on a LIVE prompt activation. Produces a NEW artifact version (the
|
|
739
|
+
* old approved artifact stays immutable), re-validates through the real
|
|
740
|
+
* scribe content contract, enqueues a NORMAL pending approval for it (the
|
|
741
|
+
* Owner reviews the diff + intent fields on the focus page and approves —
|
|
742
|
+
* the approve path then performs the atomic replacement). Duplicate requests
|
|
743
|
+
* are idempotent: an existing pending revision approval is returned as-is.
|
|
744
|
+
*/
|
|
745
|
+
async proposePromptRevision(input) {
|
|
746
|
+
if (!stateDbExists(this.workspaceDir)) {
|
|
747
|
+
return { ok: false, error: 'not_found' };
|
|
748
|
+
}
|
|
749
|
+
const now = new Date().toISOString();
|
|
750
|
+
const { connection } = this.createWriteContext();
|
|
751
|
+
try {
|
|
752
|
+
const activationStore = new SqliteActivationStateStore(connection);
|
|
753
|
+
const artifactStore = new SqlitePIArtifactStore(connection);
|
|
754
|
+
const livePrompt = (await activationStore.listPromptActivations())
|
|
755
|
+
.find((activation) => activation.activationId === input.activationId && activation.deactivatedAt === null);
|
|
756
|
+
if (!livePrompt) {
|
|
757
|
+
return { ok: false, error: 'activation_not_live_prompt', activationId: input.activationId };
|
|
758
|
+
}
|
|
759
|
+
const oldArtifact = await artifactStore.getArtifactById(livePrompt.artifactId);
|
|
760
|
+
if (!oldArtifact) {
|
|
761
|
+
return { ok: false, error: 'artifact_unavailable', artifactId: livePrompt.artifactId };
|
|
762
|
+
}
|
|
763
|
+
const oldSnapshot = {
|
|
764
|
+
artifactId: oldArtifact.artifactId,
|
|
765
|
+
artifactKind: oldArtifact.artifactKind,
|
|
766
|
+
sourceTaskId: oldArtifact.sourceTaskId,
|
|
767
|
+
...(oldArtifact.sourcePrincipleId !== null && oldArtifact.sourcePrincipleId !== undefined ? { sourcePrincipleId: oldArtifact.sourcePrincipleId } : {}),
|
|
768
|
+
...(oldArtifact.sourceRuleId !== null && oldArtifact.sourceRuleId !== undefined ? { sourceRuleId: oldArtifact.sourceRuleId } : {}),
|
|
769
|
+
lineageArtifactIds: oldArtifact.lineageArtifactIds,
|
|
770
|
+
validationStatus: oldArtifact.validationStatus,
|
|
771
|
+
contentJson: oldArtifact.contentJson,
|
|
772
|
+
createdAt: oldArtifact.createdAt,
|
|
773
|
+
updatedAt: oldArtifact.updatedAt,
|
|
774
|
+
};
|
|
775
|
+
// Idempotency: a PENDING approval for another artifact version of this
|
|
776
|
+
// same principle already exists → return it (no second artifact, no
|
|
777
|
+
// second approval). Rejected/approved history does not block a new try.
|
|
778
|
+
const queueStore = new SqliteApprovalQueueStore(connection);
|
|
779
|
+
const pendingApprovals = (await new ApprovalQueue(queueStore).listAll({ status: 'pending', channel: 'prompt' }));
|
|
780
|
+
for (const pending of pendingApprovals) {
|
|
781
|
+
if (pending.artifactId === oldSnapshot.artifactId)
|
|
782
|
+
continue;
|
|
783
|
+
const pendingArtifact = await artifactStore.getArtifactById(pending.artifactId);
|
|
784
|
+
if (!pendingArtifact)
|
|
785
|
+
continue;
|
|
786
|
+
const pendingSnapshot = {
|
|
787
|
+
artifactId: pendingArtifact.artifactId,
|
|
788
|
+
artifactKind: pendingArtifact.artifactKind,
|
|
789
|
+
sourceTaskId: pendingArtifact.sourceTaskId,
|
|
790
|
+
...(pendingArtifact.sourcePrincipleId !== null && pendingArtifact.sourcePrincipleId !== undefined ? { sourcePrincipleId: pendingArtifact.sourcePrincipleId } : {}),
|
|
791
|
+
...(pendingArtifact.sourceRuleId !== null && pendingArtifact.sourceRuleId !== undefined ? { sourceRuleId: pendingArtifact.sourceRuleId } : {}),
|
|
792
|
+
lineageArtifactIds: pendingArtifact.lineageArtifactIds,
|
|
793
|
+
validationStatus: pendingArtifact.validationStatus,
|
|
794
|
+
contentJson: pendingArtifact.contentJson,
|
|
795
|
+
createdAt: pendingArtifact.createdAt,
|
|
796
|
+
updatedAt: pendingArtifact.updatedAt,
|
|
797
|
+
};
|
|
798
|
+
if (isArtifactRevisionOf(pendingSnapshot, oldSnapshot)) {
|
|
799
|
+
return {
|
|
800
|
+
ok: true,
|
|
801
|
+
alreadyPending: true,
|
|
802
|
+
newArtifactId: pending.artifactId,
|
|
803
|
+
approvalId: pending.approvalId,
|
|
804
|
+
supersededActivationId: livePrompt.activationId,
|
|
805
|
+
oldStatement: '',
|
|
806
|
+
newStatement: '',
|
|
807
|
+
title: '',
|
|
808
|
+
diff: { note: 'a pending revision approval already exists for this principle' },
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
// Build + validate the new version through the REAL content contract.
|
|
813
|
+
const build = await buildOwnerRevisionArtifact({
|
|
814
|
+
oldArtifact: oldSnapshot,
|
|
815
|
+
newStatement: input.statement,
|
|
816
|
+
editedBy: input.editedBy,
|
|
817
|
+
now,
|
|
818
|
+
});
|
|
819
|
+
if (!build.ok) {
|
|
820
|
+
return { ok: false, error: 'revision_validation_failed', reason: build.error, nextAction: build.nextAction };
|
|
821
|
+
}
|
|
822
|
+
await artifactStore.upsertArtifact({
|
|
823
|
+
artifactId: build.draft.artifactId,
|
|
824
|
+
artifactKind: 'principle',
|
|
825
|
+
sourceTaskId: build.draft.sourceTaskId,
|
|
826
|
+
...(build.draft.sourcePrincipleId !== undefined ? { sourcePrincipleId: build.draft.sourcePrincipleId } : {}),
|
|
827
|
+
sourceRuleId: undefined,
|
|
828
|
+
lineageArtifactIds: build.draft.lineageArtifactIds,
|
|
829
|
+
validationStatus: 'pending',
|
|
830
|
+
contentJson: build.draft.contentJson,
|
|
831
|
+
createdAt: now,
|
|
832
|
+
updatedAt: now,
|
|
833
|
+
});
|
|
834
|
+
const validated = await artifactStore.updateValidationStatus(build.draft.artifactId, 'validated');
|
|
835
|
+
if (!validated) {
|
|
836
|
+
return { ok: false, error: 'revision_validation_failed', reason: 'updateValidationStatus returned false', nextAction: 'check pi_artifacts store integrity' };
|
|
837
|
+
}
|
|
838
|
+
// Enqueue the NORMAL approval through the dispatcher (identity gate +
|
|
839
|
+
// writer canActivate — same authority as every other prompt approval).
|
|
840
|
+
const dispatcher = new ActivationDispatcher({
|
|
841
|
+
getArtifactById: async (id) => {
|
|
842
|
+
const rec = await artifactStore.getArtifactById(id);
|
|
843
|
+
if (!rec)
|
|
844
|
+
return null;
|
|
845
|
+
return {
|
|
846
|
+
artifactId: rec.artifactId,
|
|
847
|
+
artifactKind: rec.artifactKind,
|
|
848
|
+
sourceTaskId: rec.sourceTaskId,
|
|
849
|
+
...(rec.sourcePrincipleId !== null && rec.sourcePrincipleId !== undefined ? { sourcePrincipleId: rec.sourcePrincipleId } : {}),
|
|
850
|
+
...(rec.sourceRuleId !== null && rec.sourceRuleId !== undefined ? { sourceRuleId: rec.sourceRuleId } : {}),
|
|
851
|
+
lineageArtifactIds: rec.lineageArtifactIds,
|
|
852
|
+
validationStatus: rec.validationStatus,
|
|
853
|
+
contentJson: rec.contentJson,
|
|
854
|
+
createdAt: rec.createdAt,
|
|
855
|
+
updatedAt: rec.updatedAt,
|
|
856
|
+
};
|
|
857
|
+
},
|
|
858
|
+
}, activationStore, {
|
|
859
|
+
writers: [new PromptWriter()],
|
|
860
|
+
approvalQueueStore: queueStore,
|
|
861
|
+
ledgerIdentity: {
|
|
862
|
+
ledger: new PrincipleTreeLedgerAdapter({ stateDir: `${this.workspaceDir}/.state` }),
|
|
863
|
+
getArtifactById: async (id) => {
|
|
864
|
+
const rec = await artifactStore.getArtifactById(id);
|
|
865
|
+
return rec
|
|
866
|
+
? {
|
|
867
|
+
artifactId: rec.artifactId,
|
|
868
|
+
artifactKind: rec.artifactKind,
|
|
869
|
+
sourceTaskId: rec.sourceTaskId,
|
|
870
|
+
...(rec.sourcePrincipleId !== null && rec.sourcePrincipleId !== undefined ? { sourcePrincipleId: rec.sourcePrincipleId } : {}),
|
|
871
|
+
...(rec.sourceRuleId !== null && rec.sourceRuleId !== undefined ? { sourceRuleId: rec.sourceRuleId } : {}),
|
|
872
|
+
lineageArtifactIds: rec.lineageArtifactIds,
|
|
873
|
+
validationStatus: rec.validationStatus,
|
|
874
|
+
contentJson: rec.contentJson,
|
|
875
|
+
createdAt: rec.createdAt,
|
|
876
|
+
updatedAt: rec.updatedAt,
|
|
877
|
+
}
|
|
878
|
+
: null;
|
|
879
|
+
},
|
|
880
|
+
},
|
|
881
|
+
});
|
|
882
|
+
const enqueued = await dispatcher.dispatch({
|
|
883
|
+
artifactId: build.draft.artifactId,
|
|
884
|
+
channel: 'prompt',
|
|
885
|
+
rolloutDecision: 'require_approval',
|
|
886
|
+
actor: { kind: 'human', userId: input.editedBy },
|
|
887
|
+
now,
|
|
888
|
+
confirm: true,
|
|
889
|
+
});
|
|
890
|
+
if (enqueued.decision !== 'queued_for_approval') {
|
|
891
|
+
// rc-9: the artifact stays (pending approval never happened); report why.
|
|
892
|
+
const reason = 'reason' in enqueued ? enqueued.reason : enqueued.decision;
|
|
893
|
+
return { ok: false, error: 'revision_enqueue_failed', reason: String(reason), nextAction: 'check the ledger membership of this principle, then retry' };
|
|
894
|
+
}
|
|
895
|
+
// Route-aware capacity prediction for the NEW statement (review aid).
|
|
896
|
+
let deliverability;
|
|
897
|
+
try {
|
|
898
|
+
deliverability = await checkPromptArtifactDeliverabilityById({
|
|
899
|
+
workspaceDir: this.workspaceDir,
|
|
900
|
+
artifactId: build.draft.artifactId,
|
|
901
|
+
...(input.targetHost !== undefined ? { targetHost: input.targetHost } : {}),
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
catch {
|
|
905
|
+
deliverability = undefined;
|
|
906
|
+
}
|
|
907
|
+
return {
|
|
908
|
+
ok: true,
|
|
909
|
+
alreadyPending: false,
|
|
910
|
+
newArtifactId: build.draft.artifactId,
|
|
911
|
+
approvalId: enqueued.approvalId,
|
|
912
|
+
supersededActivationId: livePrompt.activationId,
|
|
913
|
+
oldStatement: build.oldStatement,
|
|
914
|
+
newStatement: input.statement.trim(),
|
|
915
|
+
title: build.title,
|
|
916
|
+
diff: {
|
|
917
|
+
intentContract: build.intentContract,
|
|
918
|
+
},
|
|
919
|
+
...(deliverability !== undefined ? { deliverability } : {}),
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
finally {
|
|
923
|
+
try {
|
|
924
|
+
connection.close();
|
|
925
|
+
}
|
|
926
|
+
catch { /* best-effort */ }
|
|
927
|
+
}
|
|
928
|
+
}
|
|
620
929
|
// eslint-disable-next-line @typescript-eslint/class-methods-use-this -- lifecycle interface; connections are request-scoped
|
|
621
930
|
dispose() {
|
|
622
931
|
// Connections are opened and closed per-request; no persistent state.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type PromptInjectionTargetHost } from '@principles/host-runtime';
|
|
1
2
|
import type { PromptInjectionBudgetStatus } from '../../shared/prompt-injection-contract.js';
|
|
2
3
|
export interface ApprovalGroup {
|
|
3
4
|
principleId: string;
|
|
@@ -15,6 +16,12 @@ export interface ApprovalGroup {
|
|
|
15
16
|
* (the client has no access to the escaped, serialized size).
|
|
16
17
|
*/
|
|
17
18
|
fitsPromptBudget?: boolean;
|
|
19
|
+
revisionReviewUnavailable?: boolean;
|
|
20
|
+
revisionReview?: {
|
|
21
|
+
oldStatement: string;
|
|
22
|
+
newStatement: string;
|
|
23
|
+
intentFields: Record<string, string>;
|
|
24
|
+
};
|
|
18
25
|
/**
|
|
19
26
|
* PRI-940: the pinned artifact no longer exists in pi_artifacts (typically
|
|
20
27
|
* superseded by a newer scribe revision). There is no contentJson, so
|
|
@@ -42,16 +49,19 @@ export interface ApprovalsGroupedResponse {
|
|
|
42
49
|
export declare class ApprovalsGroupedConsoleModel {
|
|
43
50
|
private readonly workspaceDir;
|
|
44
51
|
constructor(workspaceDir: string);
|
|
45
|
-
getApprovalsGrouped(): Promise<ApprovalsGroupedResponse>;
|
|
52
|
+
getApprovalsGrouped(targetHost?: PromptInjectionTargetHost): Promise<ApprovalsGroupedResponse>;
|
|
46
53
|
/**
|
|
47
54
|
* PRI-908: recompute the production prompt injection projection so the
|
|
48
|
-
* focus page can forecast "approved
|
|
55
|
+
* focus page can forecast "approved → effective" BEFORE the Owner decides.
|
|
49
56
|
* PR #1844 follow-up: the forecast follows the workspace's REAL injection
|
|
50
|
-
* route
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
57
|
+
* route; PD_PROMPT_CAPACITY_V1 AC-01 additionally binds that route to host
|
|
58
|
+
* facts (declarations ∪ pain evidence) or an explicit request-level target
|
|
59
|
+
* host — a Codex workspace is never forecast on the list route, and when
|
|
60
|
+
* the applicable hosts disagree the payload says `capacityStatus:
|
|
61
|
+
* 'unconfirmed'` with per-host forecasts instead of guessing. Advisory
|
|
62
|
+
* only: a projection failure omits the field — the approve-time precheck
|
|
63
|
+
* (B1) remains the fail-loud gate (rc-9), so this must never fail the
|
|
64
|
+
* grouped read.
|
|
55
65
|
*/
|
|
56
66
|
private readPromptInjectionBudgetStatus;
|
|
57
67
|
dispose(): void;
|