@principles/workbuddy-adapter 0.2.14 → 0.2.16
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/dist/codec/input-decoder.d.ts +14 -11
- package/dist/codec/input-decoder.js +28 -14
- package/dist/pd-hook.js +36 -13
- package/dist/worker/workspace-worker.js +2 -1
- package/package.json +3 -3
|
@@ -13,18 +13,21 @@ export declare class WorkBuddyDecoderError extends Error {
|
|
|
13
13
|
* SPEC §4.2 failure normalization: WorkBuddy marks tool failure through
|
|
14
14
|
* explicit shapes — `{is_error:true,...}` (Read failures), Bash
|
|
15
15
|
* `{exitCode>0, error}` — while SUCCESS may be a bare STRING (rendered Read
|
|
16
|
-
* output).
|
|
17
|
-
* faithfully (the frozen fixtures show string === Read success; asserting
|
|
18
|
-
* success-or-failure from string-ness alone would be fabrication).
|
|
16
|
+
* output).
|
|
19
17
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
18
|
+
* SPEC-RULECODE-HOST-PROMOTION-001 R2: on the PostToolUse channel the FROZEN
|
|
19
|
+
* host contract (Phase 0 baseline; see the channel table at the top of this
|
|
20
|
+
* file) is itself completion evidence — PostToolUseFailure is a SEPARATE
|
|
21
|
+
* registration and "PostToolUse never fires for that failure class". A
|
|
22
|
+
* response with NO failure marker on THIS channel is therefore a completed
|
|
23
|
+
* success, not an unconfirmed shape: the durable tool_calls row must record
|
|
24
|
+
* `success` so the WorkBuddy rule-context provider can assemble a real
|
|
25
|
+
* history window (an `unknown` row is rejected by the shared history
|
|
26
|
+
* validator, which would leave the provider permanently unavailable on this
|
|
27
|
+
* host). String-ness ALONE still proves nothing on any other channel — the
|
|
28
|
+
* classification rides on the channel contract, and every DETECTED failure
|
|
29
|
+
* is still wrapped `{result, error}` so the shared classifier can never
|
|
30
|
+
* miss it. Missing exitCode on a detected failure NEVER defaults to success.
|
|
28
31
|
*/
|
|
29
32
|
export declare function normalizeWorkbuddyToolResponse(toolResponse: unknown, eventForDiagnostics: string): unknown;
|
|
30
33
|
export declare function decodeWorkbuddyInput(raw: unknown): HostEvent;
|
|
@@ -127,22 +127,33 @@ function field(value, key) {
|
|
|
127
127
|
* SPEC §4.2 failure normalization: WorkBuddy marks tool failure through
|
|
128
128
|
* explicit shapes — `{is_error:true,...}` (Read failures), Bash
|
|
129
129
|
* `{exitCode>0, error}` — while SUCCESS may be a bare STRING (rendered Read
|
|
130
|
-
* output).
|
|
131
|
-
* faithfully (the frozen fixtures show string === Read success; asserting
|
|
132
|
-
* success-or-failure from string-ness alone would be fabrication).
|
|
130
|
+
* output).
|
|
133
131
|
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
132
|
+
* SPEC-RULECODE-HOST-PROMOTION-001 R2: on the PostToolUse channel the FROZEN
|
|
133
|
+
* host contract (Phase 0 baseline; see the channel table at the top of this
|
|
134
|
+
* file) is itself completion evidence — PostToolUseFailure is a SEPARATE
|
|
135
|
+
* registration and "PostToolUse never fires for that failure class". A
|
|
136
|
+
* response with NO failure marker on THIS channel is therefore a completed
|
|
137
|
+
* success, not an unconfirmed shape: the durable tool_calls row must record
|
|
138
|
+
* `success` so the WorkBuddy rule-context provider can assemble a real
|
|
139
|
+
* history window (an `unknown` row is rejected by the shared history
|
|
140
|
+
* validator, which would leave the provider permanently unavailable on this
|
|
141
|
+
* host). String-ness ALONE still proves nothing on any other channel — the
|
|
142
|
+
* classification rides on the channel contract, and every DETECTED failure
|
|
143
|
+
* is still wrapped `{result, error}` so the shared classifier can never
|
|
144
|
+
* miss it. Missing exitCode on a detected failure NEVER defaults to success.
|
|
142
145
|
*/
|
|
143
146
|
export function normalizeWorkbuddyToolResponse(toolResponse, eventForDiagnostics) {
|
|
144
|
-
if (typeof toolResponse === 'string')
|
|
145
|
-
|
|
147
|
+
if (typeof toolResponse === 'string') {
|
|
148
|
+
// Wrap under a NON-`result` key: the shared classifier treats a record
|
|
149
|
+
// envelope as a structured completion, but a string inside `result` would
|
|
150
|
+
// still hit its text-only → unknown branch. `toolResponse` preserves the
|
|
151
|
+
// rendered text verbatim while the envelope carries the channel-contract
|
|
152
|
+
// completion.
|
|
153
|
+
return eventForDiagnostics === 'PostToolUse'
|
|
154
|
+
? { toolResponse }
|
|
155
|
+
: toolResponse;
|
|
156
|
+
}
|
|
146
157
|
if (!isRecord(toolResponse)) {
|
|
147
158
|
throw new WorkBuddyDecoderError(`malformed required field "tool_response"`, `${eventForDiagnostics} tool_response must be the rendered string or the engine result object from ${HOST_CONTRACT_BASELINE}.`);
|
|
148
159
|
}
|
|
@@ -152,8 +163,11 @@ export function normalizeWorkbuddyToolResponse(toolResponse, eventForDiagnostics
|
|
|
152
163
|
const errorValue = field(toolResponse, 'error');
|
|
153
164
|
const errorText = typeof errorValue === 'string' && errorValue.trim().length > 0 ? errorValue : undefined;
|
|
154
165
|
const failed = isError || (exitCode !== undefined && exitCode > 0) || errorText !== undefined;
|
|
155
|
-
if (!failed)
|
|
166
|
+
if (!failed) {
|
|
167
|
+
// An object envelope is already a structured completion for the shared
|
|
168
|
+
// classifier (normalizeOutcome: record ⇒ success) — pass through.
|
|
156
169
|
return toolResponse;
|
|
170
|
+
}
|
|
157
171
|
const toolErrorCode = field(toolResponse, 'tool_error_code');
|
|
158
172
|
let explicitError;
|
|
159
173
|
if (errorText !== undefined) {
|
package/dist/pd-hook.js
CHANGED
|
@@ -37,7 +37,7 @@ import path from 'node:path';
|
|
|
37
37
|
import process from 'node:process';
|
|
38
38
|
import { pathToFileURL, fileURLToPath } from 'node:url';
|
|
39
39
|
import { appendEventLogLine, redactTelemetryString, isRuleHostEvaluatedEventData } from '@principles/core/runtime-v2';
|
|
40
|
-
import { createProductionHostRuntime, loadPdConfigForPlugin, parseOwnerStatementPrefix, assessWorkbuddyLiveKeyword, resolveSharedCorrectionKeywordStoreForDetection, admitTrustedWorkbuddyLiveCorrection, ensureGovernanceDiagnosticianTask } from '@principles/host-runtime';
|
|
40
|
+
import { createProductionHostRuntime, createWorkbuddyRuleContextProvider, loadPdConfigForPlugin, parseOwnerStatementPrefix, assessWorkbuddyLiveKeyword, resolveSharedCorrectionKeywordStoreForDetection, admitTrustedWorkbuddyLiveCorrection, ensureGovernanceDiagnosticianTask } from '@principles/host-runtime';
|
|
41
41
|
import { computeFeatureFlagsFromConfig } from '@principles/core/runtime-v2';
|
|
42
42
|
import { WorkBuddyHooksHostAdapter } from './host-adapter.js';
|
|
43
43
|
import { WORKBUDDY_TOOL_SEMANTICS } from './tool-semantics.js';
|
|
@@ -157,16 +157,16 @@ function recordRuleHostEvaluations(stateDir, sessionId, evaluations) {
|
|
|
157
157
|
return diagnostics;
|
|
158
158
|
}
|
|
159
159
|
/**
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
160
|
+
* SPEC-RULECODE-HOST-PROMOTION-001 C: WorkBuddy now supplies a REAL V2 rule
|
|
161
|
+
* context provider over its own trajectory (see the dispatch wiring below),
|
|
162
|
+
* so the old blanket "host provides no runtime context provider" note no
|
|
163
|
+
* longer applies. The remaining honest boundary: rules whose required facts
|
|
164
|
+
* include design_approval stay suspended on this host (no WorkBuddy
|
|
165
|
+
* design-gate attestation entry exists), and unavailable history still
|
|
166
|
+
* suspends context-dependent rules through the shared-gate contract. The
|
|
167
|
+
* note below annotates exactly that residual state.
|
|
168
168
|
*/
|
|
169
|
-
const WORKBUDDY_CONTEXT_UNSUPPORTED_NOTE = '
|
|
169
|
+
const WORKBUDDY_CONTEXT_UNSUPPORTED_NOTE = 'workbuddy_runtime_context_limited: WorkBuddy resolves rule context from its own trajectory only; rules requiring design_approval facts or unavailable history stay suspended on this host';
|
|
170
170
|
export function annotateContextWarnings(warnings) {
|
|
171
171
|
return warnings.map((warning) => warning.startsWith('rule_context_v2_unavailable')
|
|
172
172
|
? `${warning}; ${WORKBUDDY_CONTEXT_UNSUPPORTED_NOTE}`
|
|
@@ -226,6 +226,10 @@ export async function processWorkbuddyHookInvocation(rawStdin, channelEventName,
|
|
|
226
226
|
}
|
|
227
227
|
return failure;
|
|
228
228
|
}
|
|
229
|
+
// Capture the decode boundary before governance or runtime work can consume
|
|
230
|
+
// time; the rule-context history window must never include rows written
|
|
231
|
+
// afterwards (same cutoff discipline as the Codex hook epoch).
|
|
232
|
+
const hookEpoch = new Date();
|
|
229
233
|
// OD-07/P06 governance workspace: explicit, user-level, shared across
|
|
230
234
|
// projects; never auto-created here and never falling back to another host.
|
|
231
235
|
const resolution = resolveWorkbuddyWorkspace(os.homedir());
|
|
@@ -243,7 +247,11 @@ export async function processWorkbuddyHookInvocation(rawStdin, channelEventName,
|
|
|
243
247
|
const { flags } = computeFeatureFlagsFromConfig(config.effective);
|
|
244
248
|
if (flags['host.workbuddy']?.enabled !== true) {
|
|
245
249
|
// OD-03: default OFF; observable skip, no dispatch, no workspace writes.
|
|
246
|
-
|
|
250
|
+
// PRI-1063 (review fix): `diagnostic()` collapses every whitespace run,
|
|
251
|
+
// so the hint is a single-line flow mapping; and since every real config
|
|
252
|
+
// already has a `features:` block, it must be ADDED to that block rather
|
|
253
|
+
// than pasted as a second top-level one (that is a duplicate-key error).
|
|
254
|
+
return neutralWithDiagnostics([diagnostic('skipped', 'host.workbuddy_disabled', 'Enable PD for WorkBuddy in the governance workspace config (~/.workbuddy/pd-workspace/.pd/config.yaml) — in the existing "features:" block set host.workbuddy: {enabled: true} (one flat key, not nested).')]);
|
|
247
255
|
}
|
|
248
256
|
const evidenceEnabled = flags.principle_receipt_ledger?.enabled === true;
|
|
249
257
|
// projectDir = the host cwd (payload cwd; SessionStart carries none);
|
|
@@ -352,6 +360,23 @@ export async function processWorkbuddyHookInvocation(rawStdin, channelEventName,
|
|
|
352
360
|
hostKind: 'workbuddy',
|
|
353
361
|
toolSemantics: WORKBUDDY_TOOL_SEMANTICS,
|
|
354
362
|
events: workbuddyEventEmitter(stateDir, evidenceContext),
|
|
363
|
+
// SPEC-RULECODE-HOST-PROMOTION-001 C: the V2 rule context provider over
|
|
364
|
+
// WorkBuddy's own observable trajectory (same-session tool_calls rows
|
|
365
|
+
// the shared afterToolCall path records). No designGate fact is
|
|
366
|
+
// supplied — WorkBuddy has no design-gate attestation entry point, so
|
|
367
|
+
// design_approval-dependent rules suspend with the precise
|
|
368
|
+
// `rule_required_fact_unavailable` warning rather than borrowing
|
|
369
|
+
// another host's declaration. Unavailable history (disabled flag, no
|
|
370
|
+
// project scope, first session turn) degrades through the shared
|
|
371
|
+
// suspension contract, observably.
|
|
372
|
+
ruleContextProvider: createWorkbuddyRuleContextProvider({
|
|
373
|
+
workspaceDir: resolution.workspaceDir,
|
|
374
|
+
...(projectDir !== undefined ? { projectDir } : {}),
|
|
375
|
+
enabled: flags.rulecode_context_v2?.enabled === true,
|
|
376
|
+
cutoffAt: hookEpoch,
|
|
377
|
+
currentToolCallId: event.context.toolCallId ?? '',
|
|
378
|
+
toolSemantics: WORKBUDDY_TOOL_SEMANTICS,
|
|
379
|
+
}),
|
|
355
380
|
// Remediation R2 (candidate-level admission): the diagnostician's
|
|
356
381
|
// candidates are admitted ONLY with input evidence (needs_evidence
|
|
357
382
|
// otherwise), and Codex-style transcript promotion does not exist on
|
|
@@ -378,8 +403,6 @@ export async function processWorkbuddyHookInvocation(rawStdin, channelEventName,
|
|
|
378
403
|
}],
|
|
379
404
|
};
|
|
380
405
|
},
|
|
381
|
-
// PRI-780 boundary: NO context provider — v2 rules stay SUSPENDED on
|
|
382
|
-
// WorkBuddy (never loaded context-blind). See annotateContextWarnings.
|
|
383
406
|
}).dispatch(event);
|
|
384
407
|
const evaluationDiagnostics = recordRuleHostEvaluations(stateDir, event.context.sessionId, result.metadata?.evaluations);
|
|
385
408
|
// Encode BEFORE recording an attributed deny: a runtime_verified
|
|
@@ -91,7 +91,8 @@ export async function runWorkbuddyWorkspaceWorkerCycle(options) {
|
|
|
91
91
|
}
|
|
92
92
|
const { flags } = computeFeatureFlagsFromConfig(config.effective);
|
|
93
93
|
if (flags['host.workbuddy']?.enabled !== true) {
|
|
94
|
-
|
|
94
|
+
// PRI-1063 (review fix): single-line flow mapping — see pd-hook.ts.
|
|
95
|
+
return { ...base, mode: 'paused', reason: 'host.workbuddy_disabled', nextAction: 'Enable WorkBuddy PD behavior in the governance workspace .pd/config.yaml — in the existing "features:" block set host.workbuddy: {enabled: true} (one flat key, not nested).' };
|
|
95
96
|
}
|
|
96
97
|
const consumerFlag = loadFeatureFlagFromConfig(workspaceDir, 'internalization_auto_consumer', { info: (m) => logger.info(m), warn: (m) => logger.warn(m) });
|
|
97
98
|
// The Diagnostician capability gate resolves from the same canonical
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@principles/workbuddy-adapter",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.16",
|
|
4
4
|
"description": "WorkBuddy host adapter for Principles Disciple — implements the HostAdapter interface for WorkBuddy's Git Bash command-hook model (ADR-0020 third host, SPEC workbuddy-host-adapter v0.3.0).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"lint": "eslint \"src/**/*.ts\""
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@principles/core": "^1.297.
|
|
46
|
-
"@principles/host-runtime": "^0.14.
|
|
45
|
+
"@principles/core": "^1.297.5",
|
|
46
|
+
"@principles/host-runtime": "^0.14.3",
|
|
47
47
|
"@principles/install-layout": "^0.4.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|