kiokuko-dsh 0.2.21 → 0.2.23

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 (151) hide show
  1. package/README.ja.md +1 -1
  2. package/README.ko.md +1 -1
  3. package/README.md +1 -1
  4. package/README.zh-CN.md +1 -1
  5. package/dist/deep-thinker/current-model.d.ts.map +1 -1
  6. package/dist/deep-thinker/current-model.js +4 -0
  7. package/dist/deep-thinker/current-model.js.map +1 -1
  8. package/dist/dsh/answer-review/contracts.d.ts +1 -0
  9. package/dist/dsh/answer-review/contracts.d.ts.map +1 -1
  10. package/dist/dsh/answer-review/coordinator.js +3 -3
  11. package/dist/dsh/answer-review/coordinator.js.map +1 -1
  12. package/dist/dsh/clarification-context.d.ts +24 -0
  13. package/dist/dsh/clarification-context.d.ts.map +1 -0
  14. package/dist/dsh/clarification-context.js +31 -0
  15. package/dist/dsh/clarification-context.js.map +1 -0
  16. package/dist/dsh/coding-question.d.ts +26 -0
  17. package/dist/dsh/coding-question.d.ts.map +1 -0
  18. package/dist/dsh/coding-question.js +48 -0
  19. package/dist/dsh/coding-question.js.map +1 -0
  20. package/dist/dsh/coding-workflow/contracts.d.ts +2 -2
  21. package/dist/dsh/coding-workflow/host.d.ts +2 -4
  22. package/dist/dsh/coding-workflow/host.d.ts.map +1 -1
  23. package/dist/dsh/coding-workflow/host.js +13 -29
  24. package/dist/dsh/coding-workflow/host.js.map +1 -1
  25. package/dist/dsh/coding-workflow/intent.d.ts.map +1 -1
  26. package/dist/dsh/coding-workflow/intent.js +2 -1
  27. package/dist/dsh/coding-workflow/intent.js.map +1 -1
  28. package/dist/dsh/coding-workflow/service.d.ts +6 -6
  29. package/dist/dsh/coding-workflow/service.d.ts.map +1 -1
  30. package/dist/dsh/coding-workflow/service.js +5 -3
  31. package/dist/dsh/coding-workflow/service.js.map +1 -1
  32. package/dist/dsh/guardrail/contracts.d.ts +33 -1
  33. package/dist/dsh/guardrail/contracts.d.ts.map +1 -1
  34. package/dist/dsh/guardrail/contracts.js +1 -1
  35. package/dist/dsh/guardrail/contracts.js.map +1 -1
  36. package/dist/dsh/guardrail/engine.d.ts +2 -0
  37. package/dist/dsh/guardrail/engine.d.ts.map +1 -1
  38. package/dist/dsh/guardrail/engine.js +18 -7
  39. package/dist/dsh/guardrail/engine.js.map +1 -1
  40. package/dist/dsh/guardrail/evidence.d.ts.map +1 -1
  41. package/dist/dsh/guardrail/evidence.js +29 -1
  42. package/dist/dsh/guardrail/evidence.js.map +1 -1
  43. package/dist/dsh/guardrail/service.d.ts +2 -0
  44. package/dist/dsh/guardrail/service.d.ts.map +1 -1
  45. package/dist/dsh/guardrail/service.js +44 -2
  46. package/dist/dsh/guardrail/service.js.map +1 -1
  47. package/dist/dsh/lisp/ci.d.ts +7 -0
  48. package/dist/dsh/lisp/ci.d.ts.map +1 -1
  49. package/dist/dsh/lisp/ci.js +27 -10
  50. package/dist/dsh/lisp/ci.js.map +1 -1
  51. package/dist/dsh/lisp/coding-choice.d.ts +2 -0
  52. package/dist/dsh/lisp/coding-choice.d.ts.map +1 -1
  53. package/dist/dsh/lisp/coding-choice.js +5 -1
  54. package/dist/dsh/lisp/coding-choice.js.map +1 -1
  55. package/dist/dsh/lisp/coding-preparation.d.ts +4 -5
  56. package/dist/dsh/lisp/coding-preparation.d.ts.map +1 -1
  57. package/dist/dsh/lisp/coding-preparation.js +57 -18
  58. package/dist/dsh/lisp/coding-preparation.js.map +1 -1
  59. package/dist/dsh/lisp/compiled-cache.d.ts.map +1 -1
  60. package/dist/dsh/lisp/compiled-cache.js +4 -3
  61. package/dist/dsh/lisp/compiled-cache.js.map +1 -1
  62. package/dist/dsh/lisp/host-tool-dispatch.d.ts +19 -0
  63. package/dist/dsh/lisp/host-tool-dispatch.d.ts.map +1 -0
  64. package/dist/dsh/lisp/host-tool-dispatch.js +23 -0
  65. package/dist/dsh/lisp/host-tool-dispatch.js.map +1 -0
  66. package/dist/dsh/lisp/manager.d.ts +3 -0
  67. package/dist/dsh/lisp/manager.d.ts.map +1 -1
  68. package/dist/dsh/lisp/manager.js +114 -13
  69. package/dist/dsh/lisp/manager.js.map +1 -1
  70. package/dist/dsh/lisp/sandbox.d.ts.map +1 -1
  71. package/dist/dsh/lisp/sandbox.js +4 -2
  72. package/dist/dsh/lisp/sandbox.js.map +1 -1
  73. package/dist/dsh/lisp/surface.d.ts.map +1 -1
  74. package/dist/dsh/lisp/surface.js +62 -25
  75. package/dist/dsh/lisp/surface.js.map +1 -1
  76. package/dist/dsh/lisp/worker.d.ts +10 -2
  77. package/dist/dsh/lisp/worker.d.ts.map +1 -1
  78. package/dist/dsh/lisp/worker.js +21 -11
  79. package/dist/dsh/lisp/worker.js.map +1 -1
  80. package/dist/dsh/managed-operation-journal.d.ts +115 -0
  81. package/dist/dsh/managed-operation-journal.d.ts.map +1 -0
  82. package/dist/dsh/managed-operation-journal.js +336 -0
  83. package/dist/dsh/managed-operation-journal.js.map +1 -0
  84. package/dist/dsh/managed-operation-runner.d.ts +33 -0
  85. package/dist/dsh/managed-operation-runner.d.ts.map +1 -0
  86. package/dist/dsh/managed-operation-runner.js +104 -0
  87. package/dist/dsh/managed-operation-runner.js.map +1 -0
  88. package/dist/dsh/model-routing.d.ts +8 -4
  89. package/dist/dsh/model-routing.d.ts.map +1 -1
  90. package/dist/dsh/model-routing.js +81 -22
  91. package/dist/dsh/model-routing.js.map +1 -1
  92. package/dist/dsh/models/surface.d.ts.map +1 -1
  93. package/dist/dsh/models/surface.js +7 -1
  94. package/dist/dsh/models/surface.js.map +1 -1
  95. package/dist/dsh/models/vendor/opencode-adapter.d.ts.map +1 -1
  96. package/dist/dsh/models/vendor/opencode-adapter.js +2 -0
  97. package/dist/dsh/models/vendor/opencode-adapter.js.map +1 -1
  98. package/dist/dsh/on-demand-intake.d.ts +12 -0
  99. package/dist/dsh/on-demand-intake.d.ts.map +1 -1
  100. package/dist/dsh/on-demand-intake.js +61 -9
  101. package/dist/dsh/on-demand-intake.js.map +1 -1
  102. package/dist/dsh/owned-execution.d.ts +2 -0
  103. package/dist/dsh/owned-execution.d.ts.map +1 -1
  104. package/dist/dsh/owned-execution.js +242 -110
  105. package/dist/dsh/owned-execution.js.map +1 -1
  106. package/dist/dsh/request-interpretation-guidance.d.ts +26 -0
  107. package/dist/dsh/request-interpretation-guidance.d.ts.map +1 -0
  108. package/dist/dsh/request-interpretation-guidance.js +35 -0
  109. package/dist/dsh/request-interpretation-guidance.js.map +1 -0
  110. package/dist/dsh/semantic-compaction/coordinator.d.ts.map +1 -1
  111. package/dist/dsh/semantic-compaction/coordinator.js +15 -1
  112. package/dist/dsh/semantic-compaction/coordinator.js.map +1 -1
  113. package/dist/dsh/task-completion-guidance.d.ts +25 -0
  114. package/dist/dsh/task-completion-guidance.d.ts.map +1 -0
  115. package/dist/dsh/task-completion-guidance.js +36 -0
  116. package/dist/dsh/task-completion-guidance.js.map +1 -0
  117. package/dist/dsh/task-completion-host.d.ts.map +1 -1
  118. package/dist/dsh/task-completion-host.js +17 -7
  119. package/dist/dsh/task-completion-host.js.map +1 -1
  120. package/dist/dsh/task-completion.d.ts +26 -2
  121. package/dist/dsh/task-completion.d.ts.map +1 -1
  122. package/dist/dsh/task-completion.js +27 -13
  123. package/dist/dsh/task-completion.js.map +1 -1
  124. package/dist/enno-oduno/coding-workflow.d.ts +1 -1
  125. package/dist/locale/en.json +1 -1
  126. package/dist/locale/ja.json +1 -1
  127. package/dist/locale/ko.json +1 -1
  128. package/dist/locale/zh-CN.json +1 -1
  129. package/docs/current-upstream-compatibility-report.md +85 -0
  130. package/docs/dsh-plugin.md +6 -2
  131. package/docs/getting-started.ja.md +1 -1
  132. package/docs/getting-started.md +1 -1
  133. package/docs/guardrail.md +11 -4
  134. package/docs/lisp-prototype-planning.md +1 -1
  135. package/docs/lisp.md +1 -1
  136. package/docs/managed-operation-journal.md +21 -0
  137. package/docs/memory-lifecycle-verification.md +5 -4
  138. package/docs/tool-exposure.md +2 -0
  139. package/guardrail/aarch64-apple-darwin/kiokuko-guardrail +0 -0
  140. package/guardrail/aarch64-apple-darwin/manifest.json +14 -18
  141. package/guardrail/aarch64-unknown-linux-musl/kiokuko-guardrail +0 -0
  142. package/guardrail/aarch64-unknown-linux-musl/manifest.json +14 -16
  143. package/guardrail/build-records.json +198 -0
  144. package/guardrail/manifest.json +25 -137
  145. package/guardrail/x86_64-unknown-linux-musl/kiokuko-guardrail +0 -0
  146. package/guardrail/x86_64-unknown-linux-musl/manifest.json +14 -15
  147. package/migrations/047_managed_operation_journal.sql +21 -0
  148. package/package.json +7 -3
  149. package/scripts/guardrail-artifacts.d.mts +3 -0
  150. package/scripts/guardrail-artifacts.mjs +34 -2
  151. package/scripts/repair-continuation-source.mjs +11 -25
@@ -1,3 +1,5 @@
1
+ import { ManagedOperationJournal } from '../managed-operation-journal.js';
2
+ import { runManagedOperation, observeManagedResult, managedAdmissionDenied, isManagedAdmissionDenied } from '../managed-operation-runner.js';
1
3
  import { LispRecoverySession } from './recovery.js';
2
4
  import { CodeIntelligenceAdapterV1 } from './code-intelligence.js';
3
5
  import { CODE_METHODS } from './code-intelligence-contracts.js';
@@ -142,6 +144,12 @@ export class LispManager {
142
144
  for (const session of await this.#store.start())
143
145
  this.enabled.set(session.session_id, session.root_path);
144
146
  await this.#hot.catalog.expire();
147
+ await this.#store.database(db => {
148
+ const journal = new ManagedOperationJournal(db);
149
+ for (const row of db.prepare(`SELECT scope_id FROM dsh_managed_scopes WHERE json_extract(scope_json,'$.runtimeIdentity') LIKE 'sbcl:%'`).all()) {
150
+ journal.recover(row.scope_id);
151
+ }
152
+ });
145
153
  }
146
154
  catch (error) {
147
155
  await unlink(lockPath);
@@ -293,21 +301,21 @@ export class LispManager {
293
301
  if (this.#config.executionMode === 'protected') {
294
302
  const canary = join(base, 'host-canary');
295
303
  await writeFile(canary, 'protected', { mode: 0o600 });
296
- const probe = await worker.request('eval', { inputs: [], code: `(let ((read-denied (handler-case (progn (with-open-file (s ${JSON.stringify(canary)}) (read-char s)) nil) (file-error () t))) (delete-denied (handler-case (progn (delete-file ${JSON.stringify(canary)}) nil) (file-error () t)))) (unless (and read-denied delete-denied) (error "ISOLATION_FAILED")) :protected)` }, this.#config.startupTimeoutMs, combined);
304
+ const probe = await this.journalRequest(state, worker, 'eval', { inputs: [], code: `(let ((read-denied (handler-case (progn (with-open-file (s ${JSON.stringify(canary)}) (read-char s)) nil) (file-error () t))) (delete-denied (handler-case (progn (delete-file ${JSON.stringify(canary)}) nil) (file-error () t)))) (unless (and read-denied delete-denied) (error "ISOLATION_FAILED")) :protected)` }, this.#config.startupTimeoutMs, combined);
297
305
  if (!probe.ok || await readFile(canary, 'utf8') !== 'protected')
298
306
  fail('ISOLATION_FAILED', 'OS によるファイル保護を確認できません。');
299
307
  }
300
308
  assertCurrent();
301
309
  state.state = 'EVALUATING';
302
310
  if (mode === 'compile') {
303
- const checked = await worker.request('task-compile', { code }, this.#config.timeoutMs, combined);
311
+ const checked = await this.journalRequest(state, worker, 'task-compile', { code }, this.#config.timeoutMs, combined);
304
312
  assertCurrent();
305
313
  if (!checked.ok || checked.proposals.length)
306
314
  fail('TASK_COMPILE_FAILED', String(checked.value).slice(0, 1000));
307
315
  return { value: null, output: worker.output(), generation: worker.generation };
308
316
  }
309
317
  if (mode === 'eval') {
310
- const evaluated = await worker.request('eval', { code, inputs: [] }, this.#config.timeoutMs, combined);
318
+ const evaluated = await this.journalRequest(state, worker, 'eval', { code, inputs: [] }, this.#config.timeoutMs, combined);
311
319
  assertCurrent();
312
320
  if (!evaluated.ok || evaluated.proposals.length)
313
321
  fail('TASK_EVAL_FAILED', String(evaluated.value).slice(0, 1000));
@@ -321,7 +329,7 @@ export class LispManager {
321
329
  await writeFile(inputPath, encoded, { flag: 'wx', mode: 0o400 });
322
330
  assertCurrent();
323
331
  state.state = 'EVALUATING';
324
- const response = await worker.request('task', { code, input: inputPath }, this.#config.timeoutMs, combined);
332
+ const response = await this.journalRequest(state, worker, 'task', { code, input: inputPath }, this.#config.timeoutMs, combined);
325
333
  if (!response.ok || response.proposals.length)
326
334
  fail('TASK_EXECUTION_FAILED', String(response.value).slice(0, 1000));
327
335
  const result = await snapshot(layout.scratch, 'task-result.json', []);
@@ -380,7 +388,7 @@ export class LispManager {
380
388
  layout.compiled = compiled.path;
381
389
  layout.workspace = state.owner.root;
382
390
  layout.protected = this.#config.executionMode === 'protected';
383
- worker = new LispWorker(layout, this.#config, (method, args, context) => this.bridge(state, method, args, context), undefined, () => this.taskAuthority.bound(state.execution, async () => { await this.taskAuthority.authorizeEffect(state.owner, this.#config.executionMode === 'protected' ? 'protected' : 'direct'); }));
391
+ worker = new LispWorker(layout, this.#config, (method, args, context) => this.bridge(state, method, args, context), undefined, () => this.taskAuthority.bound(state.execution, async () => { await this.taskAuthority.authorizeEffect(state.owner, this.#config.executionMode === 'protected' ? 'protected' : 'direct'); }), (input, launch) => this.journalProtectedJob(state, input, launch));
384
392
  state.worker = worker;
385
393
  if (this.#closed || state.state !== 'PREFLIGHT' || abort.signal.aborted)
386
394
  fail('CANCELLED', '起動は取り消されています。');
@@ -390,7 +398,7 @@ export class LispManager {
390
398
  const canary = join(base, 'host-canary');
391
399
  await writeFile(canary, 'protected', { mode: 0o600 });
392
400
  const quote = (value) => JSON.stringify(value);
393
- const probe = await worker.request('eval', { inputs: [], code: `(let ((read-denied (handler-case (progn (with-open-file (s ${quote(canary)}) (read-char s)) nil) (file-error () t))) (delete-denied (handler-case (progn (delete-file ${quote(canary)}) nil) (file-error () t)))) (unless (and read-denied delete-denied) (error "ISOLATION_FAILED")) (kioku.files:write-text (merge-pathnames "probe" (kioku.files:scratch)) "ok") :protected)` }, this.#config.startupTimeoutMs);
401
+ const probe = await this.journalRequest(state, worker, 'eval', { inputs: [], code: `(let ((read-denied (handler-case (progn (with-open-file (s ${quote(canary)}) (read-char s)) nil) (file-error () t))) (delete-denied (handler-case (progn (delete-file ${quote(canary)}) nil) (file-error () t)))) (unless (and read-denied delete-denied) (error "ISOLATION_FAILED")) (kioku.files:write-text (merge-pathnames "probe" (kioku.files:scratch)) "ok") :protected)` }, this.#config.startupTimeoutMs);
394
402
  if (!probe.ok || await readFile(canary, 'utf8') !== 'protected')
395
403
  fail('ISOLATION_FAILED', 'OS によるファイル保護を確認できません。');
396
404
  }
@@ -562,7 +570,7 @@ export class LispManager {
562
570
  }
563
571
  retainOwned(state, value) {
564
572
  const receipt = value?.receipt;
565
- if (receipt && typeof receipt.operationId === 'string' && !(receipt.processStopped === true && ['completed', 'cancelled'].includes(receipt.state ?? '')))
573
+ if (receipt && typeof receipt.operationId === 'string' && !(receipt.processStopped === true && ['completed', 'cancelled'].includes(receipt.state ?? '') || receipt.state === 'denied' && receipt.processStarted === false))
566
574
  (state.ownedJobs ??= new Set()).add(receipt.operationId);
567
575
  }
568
576
  async ownedDispatch(state, action) {
@@ -606,8 +614,70 @@ export class LispManager {
606
614
  state.ownedStop = stopping.finally(() => { delete state.ownedStop; });
607
615
  return state.ownedStop;
608
616
  }
617
+ async journalProtectedJob(state, input, launch) {
618
+ const scope = state.managedScope;
619
+ if (!scope)
620
+ fail('JOURNAL_SCOPE_MISSING', 'Managed evaluation collection is unavailable.');
621
+ await scope.database(db => {
622
+ if (db.prepare(`SELECT 1 FROM dsh_managed_operations o JOIN dsh_managed_scopes s ON s.scope_id=o.scope_id
623
+ WHERE json_extract(s.scope_json,'$.sessionId')=? AND json_extract(o.operation_json,'$.route')='native' AND o.state='unknown' LIMIT 1`).get(state.owner.sessionId)) {
624
+ new ManagedOperationJournal(db).markIncomplete(scope.scopeId);
625
+ fail('JOURNAL_RECONCILE_REQUIRED', 'A previous native operation is unresolved; inspect it before another effect.');
626
+ }
627
+ });
628
+ const operation = await scope.database(db => new ManagedOperationJournal(db).prepare(scope.scopeId, { route: 'native', kind: 'process', request: { input, environment: { ...process.env }, cwd: state.worker?.layout.scratch, runtime: process.version } }));
629
+ if (!await scope.database(db => new ManagedOperationJournal(db).claim(operation.operationId)))
630
+ fail('JOURNAL_REPLAY', 'Process already dispatched.');
631
+ let job;
632
+ try {
633
+ job = await launch();
634
+ }
635
+ catch (error) {
636
+ await scope.database(db => new ManagedOperationJournal(db).transition(operation.operationId, { state: isManagedAdmissionDenied(error) ? 'denied' : 'unknown' }));
637
+ throw error;
638
+ }
639
+ await scope.database(db => new ManagedOperationJournal(db).transition(operation.operationId, { state: 'running' }));
640
+ const done = job.done;
641
+ job.done = done.then(async () => {
642
+ await observeManagedResult({ ...scope, operationId: operation.operationId, kind: 'process' }, {
643
+ code: job.result?.code, receipt: { route: 'native', processStopped: job.settled === true },
644
+ });
645
+ await scope.database(db => new ManagedOperationJournal(db).setDelivery(operation.operationId, 'delivered'));
646
+ });
647
+ // Background completion persists independently of Lisp's final return value.
648
+ void job.done.catch(() => { });
649
+ return job;
650
+ }
651
+ async journalRequest(state, worker, ...args) {
652
+ const lease = state.execution?.lease ?? this.taskAuthority.current()?.lease;
653
+ const scope = await this.#store.database(db => new ManagedOperationJournal(db).createScope({
654
+ ...(lease ? { binding: { taskId: lease.taskId, workerGeneration: worker.generation, leaseAuthorityGeneration: lease.generation, leaseRevision: lease.revision } } : {}),
655
+ sessionId: state.owner.sessionId, parentId: randomUUID(), runtimeIdentity: `sbcl:${worker.generation}`,
656
+ }));
657
+ const managedScope = { database: this.#store.database, scopeId: scope.scopeId };
658
+ state.managedScope = managedScope;
659
+ try {
660
+ return await worker.request(...args);
661
+ }
662
+ finally {
663
+ if (state.managedScope === managedScope)
664
+ delete state.managedScope;
665
+ await this.#store.database(db => new ManagedOperationJournal(db).seal(scope.scopeId));
666
+ }
667
+ }
609
668
  bridge(state, method, args, context) {
610
- const pending = this.taskAuthority.bound(state.execution, () => this.bridgeBound(state, method, args, context));
669
+ const pending = this.taskAuthority.bound(state.execution, () => {
670
+ const parsed = args;
671
+ const processEffect = ['run', 'start-job', 'ci-verify'].includes(method) || method === 'tool-call' && parsed?.name === 'kioku_exec';
672
+ const file = method === 'tool-call' && ['kioku_write', 'kioku_edit', 'kioku_remove'].includes(parsed?.name ?? '');
673
+ if (!processEffect && !file)
674
+ return this.bridgeBound(state, method, args, context);
675
+ if (!state.managedScope)
676
+ fail('JOURNAL_SCOPE_MISSING', 'Managed evaluation collection is unavailable; no operation was dispatched.');
677
+ const route = method === 'run' && this.taskAuthority.current()?.nativeCommands && this.#config.executionMode === 'development' ? 'native' : 'owned';
678
+ return runManagedOperation(state.managedScope, { route, kind: file ? 'file' : 'process',
679
+ request: { method, args, cwd: state.owner.root, workerGeneration: context.generation, evaluationId: context.evaluationId, rpcId: context.rpcId, environment: { ...process.env }, runtime: process.version } }, () => this.bridgeBound(state, method, args, context));
680
+ });
611
681
  if (['run', 'start-job', 'tool-call', 'ci-verify'].includes(method)) {
612
682
  (state.ownedCalls ??= new Set()).add(pending);
613
683
  const settled = () => { state.ownedCalls?.delete(pending); };
@@ -745,8 +815,9 @@ export class LispManager {
745
815
  const active = [...state.active];
746
816
  if (active.length !== 1)
747
817
  fail('HOST_STATE', 'CI 呼び出しの実行主体を一意に確認できません。');
818
+ // This authority check precedes the CI adapter; a refusal proves no dispatch.
748
819
  if (request.kind === 'verify')
749
- await this.taskAuthority.authorizeEffect(state.owner);
820
+ await this.taskAuthority.authorizeEffect(state.owner).catch(managedAdmissionDenied);
750
821
  context.signal.throwIfAborted();
751
822
  const result = await this.options.ciCall(state.owner, request, active[0].signal, state.worker.layout.scratch);
752
823
  if (request.kind === 'verify')
@@ -814,6 +885,22 @@ export class LispManager {
814
885
  }
815
886
  return true;
816
887
  }
888
+ managedUnresolved(owner) {
889
+ return this.#store.database(db => {
890
+ // Safety decisions use the full set, never the bounded diagnostic page.
891
+ const counts = db.prepare(`SELECT COUNT(*) AS total, COALESCE(SUM(CASE WHEN o.state='unknown' THEN 1 ELSE 0 END),0) AS unknown_count
892
+ FROM dsh_managed_operations o JOIN dsh_managed_scopes s ON s.scope_id=o.scope_id
893
+ WHERE json_extract(s.scope_json,'$.sessionId')=? AND o.state IN ('unknown','dispatching','running','prepared')`)
894
+ .get(owner.sessionId);
895
+ const operations = db.prepare(`SELECT o.operation_json FROM dsh_managed_operations o JOIN dsh_managed_scopes s ON s.scope_id=o.scope_id
896
+ WHERE json_extract(s.scope_json,'$.sessionId')=? AND o.state IN ('unknown','dispatching','running','prepared') ORDER BY o.rowid DESC LIMIT 100`)
897
+ .all(owner.sessionId).map(row => {
898
+ const value = JSON.parse(row.operation_json);
899
+ return { id: value.operationId, scopeId: value.scopeId, route: value.route, kind: value.kind, state: value.state, ...(value.originalRef ? { originalRef: value.originalRef } : {}) };
900
+ });
901
+ return { operations, total: counts.total, unknownCount: counts.unknown_count, truncated: counts.total > operations.length };
902
+ });
903
+ }
817
904
  async status(owner, offset) {
818
905
  const policy = this.options.questions?.approvalPolicy;
819
906
  const approval = { mode: policy?.mode() ?? this.#config.approvalMode, scope: 'profile', writable: policy?.writable() ?? false };
@@ -821,13 +908,16 @@ export class LispManager {
821
908
  return { approval, enabled: false, state: 'DISABLED', recovery: '/kioku-lisp enable' };
822
909
  const state = this.entry(owner);
823
910
  const taskMode = await this.isTaskMode(owner);
911
+ const managed = await this.managedUnresolved(owner);
912
+ const managedOperations = managed.operations;
913
+ const managedReconciliation = managed.unknownCount > 0;
824
914
  const unconfirmed = [...this.#agents.values()].find(s => s.owner.sessionId === owner.sessionId && s.state === 'STOP_UNCONFIRMED');
825
915
  if (state.state === 'READY' && !state.worker?.healthy)
826
916
  this.halted(state, new LispError('WORKER_EXITED', 'Lisp が終了しています。新しい Lisp の起動前に状態を確認してください。'));
827
917
  const summary = await this.#store.operationSummaries(owner.sessionId, offset);
828
918
  const taskSummary = taskMode ? await this.#store.taskSummary(owner) : undefined;
829
919
  const operations = summary.operations.map(o => ({ id: o.operation_id, agent: o.agent_id, kind: o.kind, state: o.state, updatedAt: o.updated_at }));
830
- return { approval, enabled: true, state: unconfirmed ? 'STOP_UNCONFIRMED' : taskMode && state.state === 'RECOVERY_REQUIRED' && !state.worker ? 'TASK_READY' : state.state,
920
+ return { approval, enabled: true, managedOperations, managedOperationCount: managed.total, managedOperationsTruncated: managed.truncated, managedReconciliationRequired: managedReconciliation, state: managedReconciliation ? 'RECONCILIATION_REQUIRED' : unconfirmed ? 'STOP_UNCONFIRMED' : taskMode && state.state === 'RECOVERY_REQUIRED' && !state.worker ? 'TASK_READY' : state.state,
831
921
  generation: state.worker?.generation ?? null, supervisorPid: state.worker?.supervisorPid ?? null, error: unconfirmed?.error ?? (taskMode && !state.worker ? null : state.error ?? null),
832
922
  jobs: [...(state.worker?.jobStatus() ?? []), ...[...state.ownedJobs ?? []].map(operationId => ({ operationId, owner: 'kiokuko-execution' }))],
833
923
  executionMode: this.#config.executionMode,
@@ -838,14 +928,21 @@ export class LispManager {
838
928
  ...(taskSummary ? { task: taskSummary } : {}),
839
929
  hot: await this.#hot.status(owner),
840
930
  ...(offset === undefined ? {} : { operationCount: summary.count, pendingCount: Object.values(summary.pendingStates).reduce((a, b) => a + b, 0), pendingStates: summary.pendingStates, offset, nextOffset: offset + 10 < summary.count ? offset + 10 : null }),
841
- recovery: unconfirmed ? '停止と操作記録を照合してください。自動再実行しません。' : taskMode && !state.worker ? null : state.state === 'SUSPENDED' ? 'Lisp は休止中です。次の利用時に自動起動します。変数・関数定義は保持されません。' : state.state === 'READY' ? null : '停止理由と操作履歴を確認し、/kioku-lisp recover を実行してください。' };
931
+ recovery: managedReconciliation ? 'Managed operations remain unresolved. Inspect the listed operation IDs and original receipts; restarting or abandoning Lisp does not reconcile external effects.' : unconfirmed ? '停止と操作記録を照合してください。自動再実行しません。' : taskMode && !state.worker ? null : state.state === 'SUSPENDED' ? 'Lisp は休止中です。次の利用時に自動起動します。変数・関数定義は保持されません。' : state.state === 'READY' ? null : '停止理由と操作履歴を確認し、/kioku-lisp recover を実行してください。' };
842
932
  }
843
933
  async diagnostics(owner, id) {
844
934
  this.entry(owner);
845
935
  if (id) {
846
936
  const operation = await this.#store.get(owner, identifier.parse(id));
847
- if (!operation)
937
+ if (!operation) {
938
+ const managed = await this.#store.database(db => db.prepare(`SELECT o.operation_json FROM dsh_managed_operations o JOIN dsh_managed_scopes s ON s.scope_id=o.scope_id
939
+ WHERE o.operation_id=? AND json_extract(s.scope_json,'$.sessionId')=?`).get(id, owner.sessionId));
940
+ if (managed) {
941
+ const value = JSON.parse(managed.operation_json);
942
+ return { operationId: value.operationId, scopeId: value.scopeId, parentId: value.parentId, route: value.route, kind: value.kind, state: value.state, originalRef: value.originalRef ?? null, cause: value.cause ?? null, delivery: value.delivery };
943
+ }
848
944
  fail('UNKNOWN_OPERATION', 'この主体の操作記録がありません。');
945
+ }
849
946
  return { ...operation, payload: JSON.parse(operation.payload), result: operation.result ? JSON.parse(operation.result) : null };
850
947
  }
851
948
  const operations = await this.#store.operations(owner.sessionId);
@@ -1257,7 +1354,7 @@ export class LispManager {
1257
1354
  reserved = true;
1258
1355
  if (tool === 'lisp_eval')
1259
1356
  state.currentVerification = { operationId: id, generation: worker.generation, results: [] };
1260
- const response = await worker.request(tool === 'lisp_eval' ? 'eval' : tool === 'lisp_describe' ? 'describe' : 'inspect', args, timeout, combined);
1357
+ const response = await this.journalRequest(state, worker, tool === 'lisp_eval' ? 'eval' : tool === 'lisp_describe' ? 'describe' : 'inspect', args, timeout, combined);
1261
1358
  const result = { ok: response.ok, operationId: id, value: response.value, output: worker.output(), generation: worker.generation, proposals: response.proposals };
1262
1359
  evidence = { ...result, state: response.ok ? 'RUNNING' : 'FAILED' };
1263
1360
  await this.#store.transition(owner, id, ['RUNNING'], response.ok ? 'RUNNING' : 'FAILED', evidence);
@@ -1413,6 +1510,10 @@ export class LispManager {
1413
1510
  });
1414
1511
  }
1415
1512
  async recoverBound(owner, signal) {
1513
+ const managed = await this.managedUnresolved(owner);
1514
+ if (managed.unknownCount > 0)
1515
+ return { ok: false, code: 'RECONCILIATION_REQUIRED',
1516
+ message: 'External operation results remain unknown. Inspect the listed originals. Native results without a verifiable host receipt cannot be cleared by restarting Lisp; no command was retried.', managedOperations: managed.operations, managedOperationCount: managed.total, managedOperationsTruncated: managed.truncated };
1416
1517
  if (await this.isTaskMode(owner)) {
1417
1518
  await this.stopTaskWorkers(owner.sessionId);
1418
1519
  await Promise.allSettled([...this.#executions].filter(([, session]) => session === owner.sessionId).map(([promise]) => promise));