@principles/core 1.278.5 → 1.278.7

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.
Files changed (23) hide show
  1. package/dist/runtime-v2/__tests__/recovery-sweep-service.test.js +130 -0
  2. package/dist/runtime-v2/__tests__/recovery-sweep-service.test.js.map +1 -1
  3. package/dist/runtime-v2/adapter/__tests__/pi-ai-runtime-adapter.test.js +157 -0
  4. package/dist/runtime-v2/adapter/__tests__/pi-ai-runtime-adapter.test.js.map +1 -1
  5. package/dist/runtime-v2/adapter/__tests__/structured-output-repair.test.js +9 -0
  6. package/dist/runtime-v2/adapter/__tests__/structured-output-repair.test.js.map +1 -1
  7. package/dist/runtime-v2/adapter/output-repair-contract.d.ts +10 -0
  8. package/dist/runtime-v2/adapter/output-repair-contract.d.ts.map +1 -1
  9. package/dist/runtime-v2/adapter/output-repair-contract.js.map +1 -1
  10. package/dist/runtime-v2/adapter/pi-ai-runtime-adapter.d.ts.map +1 -1
  11. package/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js +53 -1
  12. package/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js.map +1 -1
  13. package/dist/runtime-v2/adapter/structured-output-repair.d.ts +9 -1
  14. package/dist/runtime-v2/adapter/structured-output-repair.d.ts.map +1 -1
  15. package/dist/runtime-v2/adapter/structured-output-repair.js +8 -0
  16. package/dist/runtime-v2/adapter/structured-output-repair.js.map +1 -1
  17. package/dist/runtime-v2/recovery-sweep-service.d.ts +9 -0
  18. package/dist/runtime-v2/recovery-sweep-service.d.ts.map +1 -1
  19. package/dist/runtime-v2/recovery-sweep-service.js +7 -2
  20. package/dist/runtime-v2/recovery-sweep-service.js.map +1 -1
  21. package/dist/runtime-v2/runner/__tests__/base-peer-runner-failure-details.test.js +36 -0
  22. package/dist/runtime-v2/runner/__tests__/base-peer-runner-failure-details.test.js.map +1 -1
  23. package/package.json +1 -1
@@ -5,6 +5,9 @@ const mockRecoverTask = vi.fn();
5
5
  const mockStateManagerClose = vi.fn();
6
6
  const mockInitialize = vi.fn();
7
7
  const mockAssertInitialized = vi.fn();
8
+ const mockListTasks = vi.fn();
9
+ const mockGetTask = vi.fn();
10
+ const mockUpdateTask = vi.fn();
8
11
  vi.mock('../store/runtime-state-manager.js', () => ({
9
12
  RuntimeStateManager: vi.fn().mockImplementation(function () {
10
13
  this.initialize = mockInitialize;
@@ -13,8 +16,23 @@ vi.mock('../store/runtime-state-manager.js', () => ({
13
16
  this.recoverTask = mockRecoverTask;
14
17
  this.assertInitialized = mockAssertInitialized;
15
18
  this.isInitialized = true;
19
+ this.listTasks = mockListTasks;
20
+ this.getTask = mockGetTask;
21
+ this.updateTask = mockUpdateTask;
16
22
  }),
17
23
  }));
24
+ function makeTask(overrides = {}) {
25
+ return {
26
+ taskId: 'task-x',
27
+ taskKind: 'dreamer',
28
+ status: 'failed',
29
+ createdAt: '2026-09-08T00:00:00.000Z',
30
+ updatedAt: '2026-09-08T00:00:00.000Z',
31
+ attemptCount: 1,
32
+ maxAttempts: 3,
33
+ ...overrides,
34
+ };
35
+ }
18
36
  describe('createRecoverySweepService', () => {
19
37
  beforeEach(() => {
20
38
  vi.clearAllMocks();
@@ -80,4 +98,116 @@ describe('createRecoverySweepService', () => {
80
98
  .rejects.toThrow('DB init failed');
81
99
  });
82
100
  });
101
+ describe('detectFailedTasks — recovery discovery (PRI-674)', () => {
102
+ beforeEach(() => {
103
+ vi.clearAllMocks();
104
+ vi.restoreAllMocks();
105
+ mockInitialize.mockResolvedValue(undefined);
106
+ mockStateManagerClose.mockResolvedValue(undefined);
107
+ mockAssertInitialized.mockReturnValue(undefined);
108
+ mockListTasks.mockResolvedValue([]);
109
+ });
110
+ it('returns failed diagnostician parent and diag_* stage tasks (GAP-1 regression)', async () => {
111
+ mockListTasks.mockResolvedValue([
112
+ makeTask({ taskId: 'diag-parent', taskKind: 'diagnostician', inputRef: 'pain-001' }),
113
+ makeTask({ taskId: 'diag-a', taskKind: 'diag_rootcause', inputRef: 'diag-parent' }),
114
+ makeTask({ taskId: 'diag-b', taskKind: 'diag_distiller', inputRef: 'diag-parent' }),
115
+ makeTask({ taskId: 'diag-c', taskKind: 'diag_router', inputRef: 'diag-parent' }),
116
+ ]);
117
+ const handle = await createRecoverySweepService({ workspaceDir: '/tmp/test-ws' });
118
+ const results = await handle.service.detectFailedTasks();
119
+ expect(results.map((r) => r.taskId).sort())
120
+ .toEqual(['diag-a', 'diag-b', 'diag-c', 'diag-parent']);
121
+ // inputRef is the persisted stage→parent linkage consumed by CLI
122
+ // execution guidance (PRI-674 review P2).
123
+ expect(results.find((r) => r.taskId === 'diag-a')?.inputRef).toBe('diag-parent');
124
+ expect(results.find((r) => r.taskId === 'diag-parent')?.inputRef).toBe('pain-001');
125
+ });
126
+ it('returns inputRef null when the task record has none', async () => {
127
+ mockListTasks.mockResolvedValue([
128
+ makeTask({ taskId: 'no-ref', taskKind: 'diag_router', inputRef: undefined }),
129
+ ]);
130
+ const handle = await createRecoverySweepService({ workspaceDir: '/tmp/test-ws' });
131
+ const results = await handle.service.detectFailedTasks();
132
+ expect(results).toHaveLength(1);
133
+ expect(results[0]?.inputRef).toBeNull();
134
+ });
135
+ it('still returns peer runner kinds with isExhausted computed', async () => {
136
+ mockListTasks.mockResolvedValue([
137
+ makeTask({ taskId: 'peer-dreamer', taskKind: 'dreamer', attemptCount: 1, maxAttempts: 3 }),
138
+ makeTask({ taskId: 'peer-rollout', taskKind: 'rollout_reviewer', attemptCount: 3, maxAttempts: 3 }),
139
+ ]);
140
+ const handle = await createRecoverySweepService({ workspaceDir: '/tmp/test-ws' });
141
+ const results = await handle.service.detectFailedTasks();
142
+ expect(results.map((r) => r.taskId).sort()).toEqual(['peer-dreamer', 'peer-rollout']);
143
+ expect(results.find((r) => r.taskId === 'peer-dreamer')?.isExhausted).toBe(false);
144
+ expect(results.find((r) => r.taskId === 'peer-rollout')?.isExhausted).toBe(true);
145
+ });
146
+ it('excludes failed tasks of unrelated kinds', async () => {
147
+ mockListTasks.mockResolvedValue([
148
+ makeTask({ taskId: 'other', taskKind: 'principle_candidate_intake' }),
149
+ ]);
150
+ const handle = await createRecoverySweepService({ workspaceDir: '/tmp/test-ws' });
151
+ const results = await handle.service.detectFailedTasks();
152
+ expect(results).toEqual([]);
153
+ });
154
+ });
155
+ describe('recoverFailedTask — reset semantics unchanged (PRI-674)', () => {
156
+ beforeEach(() => {
157
+ vi.clearAllMocks();
158
+ vi.restoreAllMocks();
159
+ mockInitialize.mockResolvedValue(undefined);
160
+ mockStateManagerClose.mockResolvedValue(undefined);
161
+ mockAssertInitialized.mockReturnValue(undefined);
162
+ });
163
+ it('resets a failed diagnostician task to pending with lease/attempts/result cleared', async () => {
164
+ mockGetTask.mockResolvedValue(makeTask({
165
+ taskId: 'diag-parent',
166
+ taskKind: 'diagnostician',
167
+ attemptCount: 1,
168
+ maxAttempts: 3,
169
+ lastError: 'max_attempts_exceeded',
170
+ leaseOwner: 'runner-1',
171
+ leaseExpiresAt: '2026-09-08T01:00:00.000Z',
172
+ resultRef: 'artifact://partial',
173
+ }));
174
+ mockUpdateTask.mockImplementation(async (taskId, patch) => makeTask({ taskId, taskKind: 'diagnostician', ...patch }));
175
+ const handle = await createRecoverySweepService({ workspaceDir: '/tmp/test-ws' });
176
+ const result = await handle.service.recoverFailedTask('diag-parent');
177
+ expect(mockUpdateTask).toHaveBeenCalledWith('diag-parent', {
178
+ status: 'pending',
179
+ attemptCount: 0,
180
+ maxAttempts: 3,
181
+ lastError: null,
182
+ leaseOwner: null,
183
+ leaseExpiresAt: null,
184
+ resultRef: null,
185
+ });
186
+ expect(result).toMatchObject({
187
+ taskId: 'diag-parent',
188
+ previousStatus: 'failed',
189
+ newStatus: 'pending',
190
+ forceApplied: false,
191
+ });
192
+ });
193
+ it('refuses an exhausted task without force (retry guard unchanged)', async () => {
194
+ mockGetTask.mockResolvedValue(makeTask({
195
+ taskId: 'diag-exhausted',
196
+ taskKind: 'diagnostician',
197
+ attemptCount: 3,
198
+ maxAttempts: 3,
199
+ }));
200
+ const handle = await createRecoverySweepService({ workspaceDir: '/tmp/test-ws' });
201
+ await expect(handle.service.recoverFailedTask('diag-exhausted'))
202
+ .rejects.toThrow(/exhausted max attempts/);
203
+ expect(mockUpdateTask).not.toHaveBeenCalled();
204
+ });
205
+ it('returns null for a task that is not failed', async () => {
206
+ mockGetTask.mockResolvedValue(makeTask({ taskId: 'diag-ok', status: 'succeeded' }));
207
+ const handle = await createRecoverySweepService({ workspaceDir: '/tmp/test-ws' });
208
+ const result = await handle.service.recoverFailedTask('diag-ok');
209
+ expect(result).toBeNull();
210
+ expect(mockUpdateTask).not.toHaveBeenCalled();
211
+ });
212
+ });
83
213
  //# sourceMappingURL=recovery-sweep-service.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"recovery-sweep-service.test.js","sourceRoot":"","sources":["../../../src/runtime-v2/__tests__/recovery-sweep-service.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"recovery-sweep-service.test.js","sourceRoot":"","sources":["../../../src/runtime-v2/__tests__/recovery-sweep-service.test.ts"],"names":[],"mappings":"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"}
@@ -1937,5 +1937,162 @@ describe('PiAiRuntimeAdapter', () => {
1937
1937
  expect(output?.payload).toMatchObject({ diagnosisId: 'diag-test-1' });
1938
1938
  });
1939
1939
  });
1940
+ // ── PRI-707: finish metadata preservation + truncation classification ──
1941
+ //
1942
+ // Before PRI-707 the provider's stopReason/usage were dropped whenever any
1943
+ // text was extractable, so a token-limit cut was indistinguishable from a
1944
+ // true format error ("No valid JSON found in LLM response"). These tests
1945
+ // pin the preserved evidence surfaces: error details (→ durable
1946
+ // diagnosticJson via the runner's classifyError path), telemetry payloads,
1947
+ // and the repair-prompt truncation notice.
1948
+ describe('PRI-707: finish metadata preservation and truncation classification', () => {
1949
+ function makeSchemaInput(overrides = {}) {
1950
+ return {
1951
+ agentSpec: { agentId: 'diagnostician', schemaVersion: 'v1' },
1952
+ inputPayload: 'Diagnose this pain signal',
1953
+ contextItems: [],
1954
+ timeoutMs: 60_000,
1955
+ outputSchemaRef: 'diagnostician-output-v1',
1956
+ ...overrides,
1957
+ };
1958
+ }
1959
+ /** Type-guard the repair prompt out of a completeSimple mock call (rc-2). */
1960
+ function repairPromptFromCall(call) {
1961
+ if (!Array.isArray(call) || call.length < 2)
1962
+ return undefined;
1963
+ const [context] = call.slice(1, 2);
1964
+ if (typeof context !== 'object' || context === null || !Object.hasOwn(context, 'messages'))
1965
+ return undefined;
1966
+ const { messages } = context;
1967
+ if (!Array.isArray(messages) || messages.length === 0)
1968
+ return undefined;
1969
+ const [first] = messages;
1970
+ if (typeof first !== 'object' || first === null || !Object.hasOwn(first, 'content'))
1971
+ return undefined;
1972
+ const { content } = first;
1973
+ return typeof content === 'string' ? content : undefined;
1974
+ }
1975
+ function usageWith(output) {
1976
+ return { input: 120, output, cacheRead: 0, cacheWrite: 0, totalTokens: 120 + output, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } };
1977
+ }
1978
+ it('T3+T4: finish_reason=length + usage → extraction failure carries truncated evidence (details + telemetry)', async () => {
1979
+ mockComplete.mockResolvedValueOnce(makeAssistantMessage('{"diagnosisId":"diag-trunc', {
1980
+ stopReason: 'length',
1981
+ usage: usageWith(64),
1982
+ }));
1983
+ const adapter = makeAdapter(); // free_form_only → Path 3 directly
1984
+ let caught;
1985
+ try {
1986
+ await adapter.startRun(makeSchemaInput());
1987
+ }
1988
+ catch (err) {
1989
+ caught = err instanceof PDRuntimeError ? err : undefined;
1990
+ }
1991
+ expect(caught?.category).toBe('output_invalid');
1992
+ expect(caught?.message).toContain('finish_reason=length');
1993
+ // The runner persists err.details into diagnosticJson.output_failure_details
1994
+ // (PRI-559 P0-2) — so these fields become durable failure evidence.
1995
+ expect(caught?.details).toMatchObject({
1996
+ truncated: true,
1997
+ stopReason: 'length',
1998
+ outputTokens: 64,
1999
+ });
2000
+ expect(typeof caught?.details?.nextAction).toBe('string');
2001
+ const extractionEvent = findTelemetryEvent('output_extraction_failed');
2002
+ expect(extractionEvent?.payload).toMatchObject({
2003
+ stopReason: 'length',
2004
+ outputTokens: 64,
2005
+ truncated: true,
2006
+ });
2007
+ });
2008
+ it('T5: finish_reason=stop + no extractable JSON → conservative legacy classification, no truncation claim', async () => {
2009
+ mockComplete.mockResolvedValueOnce(makeAssistantMessage('I cannot answer that.'));
2010
+ const adapter = makeAdapter();
2011
+ let caught;
2012
+ try {
2013
+ await adapter.startRun(makeSchemaInput());
2014
+ }
2015
+ catch (err) {
2016
+ caught = err instanceof PDRuntimeError ? err : undefined;
2017
+ }
2018
+ expect(caught?.category).toBe('output_invalid');
2019
+ expect(caught?.message).toBe('[output_invalid] No valid JSON found in LLM response');
2020
+ expect(caught?.details).toBeUndefined();
2021
+ const extractionEvent = findTelemetryEvent('output_extraction_failed');
2022
+ const payload = extractionEvent?.payload;
2023
+ expect(payload?.truncated).toBeUndefined();
2024
+ });
2025
+ it('T2: valid JSON + schema violation with finish_reason=stop → schema-invalid, never claimed as truncation', async () => {
2026
+ mockComplete.mockResolvedValueOnce(makeAssistantMessage('{"unexpected":true}'));
2027
+ // Repair attempts fail (unparseable) → exhausted.
2028
+ mockComplete.mockResolvedValue(makeAssistantMessage('still not json'));
2029
+ const adapter = makeAdapter();
2030
+ let caught;
2031
+ try {
2032
+ await adapter.startRun(makeSchemaInput());
2033
+ }
2034
+ catch (err) {
2035
+ caught = err instanceof PDRuntimeError ? err : undefined;
2036
+ }
2037
+ expect(caught?.category).toBe('output_invalid');
2038
+ expect(caught?.message).toContain('does not match');
2039
+ const evidencePack = caught?.details?.evidencePack;
2040
+ expect(evidencePack).toBeDefined();
2041
+ // The PRI-621 RC3 heuristic fires (object matches no required key)…
2042
+ expect(evidencePack?.truncationSuspected).toBe(true);
2043
+ // …but the definitive finish-metadata signal must NOT claim truncation.
2044
+ expect(evidencePack?.truncated).toBeUndefined();
2045
+ expect(evidencePack?.stopReason).toBe('stop');
2046
+ });
2047
+ it('T8: finish_reason=length + schema violation → repair prompt carries truncation notice and evidencePack marks truncated', async () => {
2048
+ mockComplete.mockResolvedValueOnce(makeAssistantMessage('{"unexpected":true}', { stopReason: 'length' }));
2049
+ mockComplete.mockResolvedValue(makeAssistantMessage('still not json'));
2050
+ const adapter = makeAdapter();
2051
+ let caught;
2052
+ try {
2053
+ await adapter.startRun(makeSchemaInput());
2054
+ }
2055
+ catch (err) {
2056
+ caught = err instanceof PDRuntimeError ? err : undefined;
2057
+ }
2058
+ const evidencePack = caught?.details?.evidencePack;
2059
+ expect(evidencePack?.truncated).toBe(true);
2060
+ expect(evidencePack?.stopReason).toBe('length');
2061
+ // Terminal telemetry must carry the same truncation evidence so
2062
+ // monitoring can distinguish a token-limit cut from an ordinary
2063
+ // schema failure (PR #1574 review finding). Default helper usage
2064
+ // reports output: 5.
2065
+ const exhaustedEvent = findTelemetryEvent('output_repair_exhausted');
2066
+ expect(exhaustedEvent?.payload).toMatchObject({
2067
+ stopReason: 'length',
2068
+ truncated: true,
2069
+ outputTokens: 5,
2070
+ });
2071
+ // The FIRST completeSimple call is the original generation; repair
2072
+ // calls follow. Their user message must carry the truncation notice so
2073
+ // the repair LLM stops treating a cut fragment as a misunderstanding.
2074
+ const repairCalls = mockComplete.mock.calls.slice(1);
2075
+ expect(repairCalls.length).toBeGreaterThan(0);
2076
+ const firstRepairPrompt = repairPromptFromCall(repairCalls[0]);
2077
+ expect(firstRepairPrompt).toContain('TRUNCATION NOTICE');
2078
+ expect(firstRepairPrompt).toContain('finish_reason=length');
2079
+ });
2080
+ it('truncation notice absent when finish metadata shows a clean stop (repair prompt unchanged)', async () => {
2081
+ mockComplete.mockResolvedValueOnce(makeAssistantMessage('{"unexpected":true}'));
2082
+ mockComplete.mockResolvedValue(makeAssistantMessage('still not json'));
2083
+ const adapter = makeAdapter();
2084
+ try {
2085
+ await adapter.startRun(makeSchemaInput());
2086
+ }
2087
+ catch {
2088
+ // output_invalid expected — assertion is on the prompt below.
2089
+ }
2090
+ const repairCalls = mockComplete.mock.calls.slice(1);
2091
+ expect(repairCalls.length).toBeGreaterThan(0);
2092
+ const firstRepairPrompt = repairPromptFromCall(repairCalls[0]);
2093
+ expect(firstRepairPrompt).toBeDefined();
2094
+ expect(firstRepairPrompt).not.toContain('TRUNCATION NOTICE');
2095
+ });
2096
+ });
1940
2097
  });
1941
2098
  //# sourceMappingURL=pi-ai-runtime-adapter.test.js.map