@phyzess/pappra 0.1.0-beta.13 → 0.1.0-beta.18
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/README.md +134 -15
- package/assets/integrations/skills/pappra/SKILL.md +28 -7
- package/assets/integrations/skills/pappra/references/collaboration.md +633 -0
- package/assets/integrations/skills/pappra/references/environment.md +360 -0
- package/assets/integrations/skills/pappra/references/hosts.md +20 -0
- package/assets/integrations/skills/pappra/references/operations.md +36 -5
- package/dist/adapters/pi/recovery-extension.d.ts +65 -0
- package/dist/adapters/pi/recovery-extension.js +238 -0
- package/dist/adapters/pi/session-transition-extension.d.ts +30 -0
- package/dist/adapters/pi/session-transition-extension.js +265 -0
- package/dist/app/automatic-handoff-events.d.ts +26 -0
- package/dist/app/automatic-handoff-events.js +134 -0
- package/dist/app/bounded-file.d.ts +2 -0
- package/dist/app/bounded-file.js +2 -0
- package/dist/app/capability-evidence/claude.d.ts +37 -0
- package/dist/app/capability-evidence/claude.js +206 -0
- package/dist/app/capability-evidence/codex.d.ts +35 -0
- package/dist/app/capability-evidence/codex.js +193 -0
- package/dist/app/capability-evidence/index.d.ts +33 -0
- package/dist/app/capability-evidence/index.js +117 -0
- package/dist/app/capability-evidence/pi.d.ts +73 -0
- package/dist/app/capability-evidence/pi.js +669 -0
- package/dist/app/capability-evidence/shared.d.ts +69 -0
- package/dist/app/capability-evidence/shared.js +195 -0
- package/dist/app/collab-auto-handoff.d.ts +55 -0
- package/dist/app/collab-auto-handoff.js +384 -0
- package/dist/app/collab-bindings.d.ts +57 -0
- package/dist/app/collab-bindings.js +1006 -0
- package/dist/app/collab-bootstrap-transport.d.ts +163 -0
- package/dist/app/collab-bootstrap-transport.js +769 -0
- package/dist/app/collab-bootstrap.d.ts +137 -0
- package/dist/app/collab-bootstrap.js +3455 -0
- package/dist/app/collab-handoff.d.ts +12 -0
- package/dist/app/collab-handoff.js +511 -0
- package/dist/app/collab-messages.d.ts +2 -0
- package/dist/app/collab-messages.js +2 -0
- package/dist/app/collab-notify.d.ts +156 -0
- package/dist/app/collab-notify.js +1208 -0
- package/dist/app/collab-preflight.d.ts +34 -0
- package/dist/app/collab-preflight.js +96 -0
- package/dist/app/collab-recovery-executor.d.ts +161 -0
- package/dist/app/collab-recovery-executor.js +1153 -0
- package/dist/app/collab-recovery-projection.d.ts +103 -0
- package/dist/app/collab-recovery-projection.js +659 -0
- package/dist/app/collab-recovery.d.ts +25 -0
- package/dist/app/collab-recovery.js +429 -0
- package/dist/app/collab-resources.d.ts +16 -0
- package/dist/app/collab-resources.js +278 -0
- package/dist/app/collab-role-events.d.ts +22 -0
- package/dist/app/collab-role-events.js +329 -0
- package/dist/app/collab-role-orchestration.d.ts +53 -0
- package/dist/app/collab-role-orchestration.js +601 -0
- package/dist/app/collab-role-plan.d.ts +81 -0
- package/dist/app/collab-role-plan.js +504 -0
- package/dist/app/collab-role-repair.d.ts +109 -0
- package/dist/app/collab-role-repair.js +821 -0
- package/dist/app/collab-rollover.d.ts +263 -0
- package/dist/app/collab-rollover.js +1112 -0
- package/dist/app/collab-route.d.ts +64 -0
- package/dist/app/collab-route.js +240 -0
- package/dist/app/collab-session-transition.d.ts +221 -0
- package/dist/app/collab-session-transition.js +3334 -0
- package/dist/app/collab-state.d.ts +7 -0
- package/dist/app/collab-state.js +162 -0
- package/dist/app/collab-task-store.d.ts +22 -0
- package/dist/app/collab-task-store.js +204 -0
- package/dist/app/collab-work-execution.d.ts +127 -0
- package/dist/app/collab-work-execution.js +1027 -0
- package/dist/app/collab-work-plan.d.ts +87 -0
- package/dist/app/collab-work-plan.js +437 -0
- package/dist/app/collaboration.d.ts +8 -2
- package/dist/app/collaboration.js +101 -12
- package/dist/app/doctor.js +108 -5
- package/dist/app/host-mcp.js +34 -8
- package/dist/app/host-probe.js +4 -4
- package/dist/app/init.d.ts +58 -4
- package/dist/app/init.js +768 -155
- package/dist/app/managed-pi-bootstrap-admission.d.ts +14 -0
- package/dist/app/managed-pi-bootstrap-admission.js +104 -0
- package/dist/app/managed-pi-bootstrap-refresh.d.ts +10 -0
- package/dist/app/managed-pi-bootstrap-refresh.js +78 -0
- package/dist/app/managed-pi-delivery.d.ts +8 -0
- package/dist/app/managed-pi-delivery.js +110 -0
- package/dist/app/managed-pi-handoff-receiver.d.ts +15 -0
- package/dist/app/managed-pi-handoff-receiver.js +263 -0
- package/dist/app/managed-pi-handoff-reference.d.ts +3 -0
- package/dist/app/managed-pi-handoff-reference.js +60 -0
- package/dist/app/managed-pi-herdr.d.ts +21 -0
- package/dist/app/managed-pi-herdr.js +185 -0
- package/dist/app/managed-pi-initial-admission.d.ts +28 -0
- package/dist/app/managed-pi-initial-admission.js +416 -0
- package/dist/app/managed-pi-initial-launch.d.ts +5 -0
- package/dist/app/managed-pi-initial-launch.js +61 -0
- package/dist/app/managed-pi-native-observation.d.ts +36 -0
- package/dist/app/managed-pi-native-observation.js +191 -0
- package/dist/app/managed-pi-readiness.d.ts +29 -0
- package/dist/app/managed-pi-readiness.js +288 -0
- package/dist/app/managed-pi-repair-stop.d.ts +21 -0
- package/dist/app/managed-pi-repair-stop.js +371 -0
- package/dist/app/managed-pi-resume-admission.d.ts +32 -0
- package/dist/app/managed-pi-resume-admission.js +618 -0
- package/dist/app/managed-pi-retirement.d.ts +28 -0
- package/dist/app/managed-pi-retirement.js +112 -0
- package/dist/app/managed-pi-sdk-control.d.ts +7 -0
- package/dist/app/managed-pi-sdk-control.js +169 -0
- package/dist/app/managed-pi-sdk-runtime.d.ts +3 -0
- package/dist/app/managed-pi-sdk-runtime.js +483 -0
- package/dist/app/managed-pi-source-admission.d.ts +14 -0
- package/dist/app/managed-pi-source-admission.js +323 -0
- package/dist/app/managed-pi-transition-host.d.ts +36 -0
- package/dist/app/managed-pi-transition-host.js +292 -0
- package/dist/app/managed-pi.d.ts +54 -0
- package/dist/app/managed-pi.js +248 -0
- package/dist/app/shell-readiness.d.ts +66 -0
- package/dist/app/shell-readiness.js +368 -0
- package/dist/app/task-pin.d.ts +2 -23
- package/dist/app/task-pin.js +2 -113
- package/dist/cli/commands/collaboration-binding.d.ts +13 -0
- package/dist/cli/commands/collaboration-binding.js +118 -0
- package/dist/cli/commands/collaboration-bootstrap.d.ts +10 -0
- package/dist/cli/commands/collaboration-bootstrap.js +84 -0
- package/dist/cli/commands/collaboration-capabilities.d.ts +13 -0
- package/dist/cli/commands/collaboration-capabilities.js +127 -0
- package/dist/cli/commands/collaboration-handoff.d.ts +13 -0
- package/dist/cli/commands/collaboration-handoff.js +102 -0
- package/dist/cli/commands/collaboration-message.d.ts +17 -0
- package/dist/cli/commands/collaboration-message.js +177 -0
- package/dist/cli/commands/collaboration-plan.d.ts +8 -0
- package/dist/cli/commands/collaboration-plan.js +108 -0
- package/dist/cli/commands/collaboration-preflight.d.ts +10 -0
- package/dist/cli/commands/collaboration-preflight.js +167 -0
- package/dist/cli/commands/collaboration-recovery-run.d.ts +46 -0
- package/dist/cli/commands/collaboration-recovery-run.js +224 -0
- package/dist/cli/commands/collaboration-recovery.d.ts +11 -0
- package/dist/cli/commands/collaboration-recovery.js +104 -0
- package/dist/cli/commands/collaboration-resources.d.ts +11 -0
- package/dist/cli/commands/collaboration-resources.js +96 -0
- package/dist/cli/commands/collaboration-role-plan.d.ts +9 -0
- package/dist/cli/commands/collaboration-role-plan.js +69 -0
- package/dist/cli/commands/collaboration-role-run.d.ts +11 -0
- package/dist/cli/commands/collaboration-role-run.js +128 -0
- package/dist/cli/commands/collaboration-rollover.d.ts +10 -0
- package/dist/cli/commands/collaboration-rollover.js +103 -0
- package/dist/cli/commands/collaboration-state.d.ts +10 -0
- package/dist/cli/commands/collaboration-state.js +96 -0
- package/dist/cli/commands/collaboration-transition.d.ts +10 -0
- package/dist/cli/commands/collaboration-transition.js +75 -0
- package/dist/cli/commands/collaboration-work-run.d.ts +34 -0
- package/dist/cli/commands/collaboration-work-run.js +178 -0
- package/dist/cli/commands/collaboration.d.ts +4 -0
- package/dist/cli/commands/collaboration.js +39 -4
- package/dist/cli/commands/continuity.js +2 -6
- package/dist/cli/commands/docs.d.ts +2 -0
- package/dist/cli/commands/docs.js +89 -15
- package/dist/cli/commands/host.d.ts +7 -1
- package/dist/cli/commands/host.js +122 -7
- package/dist/cli/commands/init.d.ts +20 -3
- package/dist/cli/commands/init.js +897 -232
- package/dist/cli/commands/mcp.d.ts +1 -0
- package/dist/cli/commands/mcp.js +11 -5
- package/dist/cli/commands/repair.d.ts +12 -0
- package/dist/cli/commands/repair.js +41 -0
- package/dist/cli/commands/runtime.d.ts +1 -1
- package/dist/cli/commands/runtime.js +139 -38
- package/dist/cli/commands/session.d.ts +7 -0
- package/dist/cli/commands/session.js +82 -0
- package/dist/cli/commands/task.js +19 -5
- package/dist/cli/run.js +866 -24
- package/dist/code/identity.d.ts +7 -0
- package/dist/code/identity.js +7 -3
- package/dist/code/registration.js +7 -0
- package/dist/code/service.d.ts +9 -0
- package/dist/code/service.js +16 -3
- package/dist/code/text.js +19 -7
- package/dist/collab/admission.d.ts +91 -66
- package/dist/collab/admission.js +21 -6
- package/dist/collab/canonical.d.ts +1 -1
- package/dist/collab/canonical.js +44 -3
- package/dist/collab/envelope.d.ts +2 -2
- package/dist/collab/envelope.js +12 -12
- package/dist/collab/first-work.d.ts +3 -0
- package/dist/collab/first-work.js +15 -0
- package/dist/collab/handoff.d.ts +16 -0
- package/dist/collab/handoff.js +39 -5
- package/dist/collab/index.d.ts +97 -1
- package/dist/collab/index.js +9 -1
- package/dist/collab/messages.js +126 -3
- package/dist/collab/persisted-messages.d.ts +123 -0
- package/dist/collab/persisted-messages.js +496 -0
- package/dist/collab/preflight.d.ts +1 -1
- package/dist/collab/resources.d.ts +13 -3
- package/dist/collab/resources.js +37 -6
- package/dist/collab/rollover.d.ts +311 -0
- package/dist/collab/rollover.js +1024 -0
- package/dist/collab/store.d.ts +1 -0
- package/dist/collab/store.js +1 -4
- package/dist/collab/types.d.ts +2 -354
- package/dist/collab/validation.d.ts +2 -10
- package/dist/collab/validation.js +2 -303
- package/dist/collab/work-plan.d.ts +105 -0
- package/dist/collab/work-plan.js +843 -0
- package/dist/continuity/context.d.ts +11 -2
- package/dist/continuity/context.js +100 -13
- package/dist/continuity/handoff-bundle.d.ts +296 -0
- package/dist/continuity/handoff-bundle.js +477 -0
- package/dist/continuity/index.d.ts +2 -0
- package/dist/continuity/index.js +1 -0
- package/dist/continuity/paths.js +10 -2
- package/dist/continuity/service.js +91 -15
- package/dist/continuity/task-pin.d.ts +24 -0
- package/dist/continuity/task-pin.js +590 -0
- package/dist/doctor.d.ts +1 -0
- package/dist/doctor.js +6 -0
- package/dist/experimental/index.d.ts +13 -0
- package/dist/experimental/index.js +25 -0
- package/dist/experimental/pi.d.ts +4 -0
- package/dist/experimental/pi.js +12 -0
- package/dist/hosts/claude/native.js +1 -1
- package/dist/hosts/codex/native.js +1 -1
- package/dist/hosts/codex/protocol.js +1 -1
- package/dist/hosts/integration/bootstrap-config.d.ts +58 -0
- package/dist/hosts/integration/bootstrap-config.js +254 -0
- package/dist/hosts/integration/bundle-export.d.ts +44 -0
- package/dist/hosts/integration/bundle-export.js +366 -3
- package/dist/hosts/integration/bundle.js +3 -0
- package/dist/hosts/integration/hook.js +36 -9
- package/dist/hosts/integration/pappra-bootstrap-extension.mjs +80 -0
- package/dist/hosts/integration/pappra-extension-runtime.mjs +59 -24631
- package/dist/hosts/integration/pappra-hook.mjs +61 -24749
- package/dist/hosts/integration/pappra-recovery-extension.mjs +259 -0
- package/dist/hosts/integration/pappra-session-transition-extension.mjs +276 -0
- package/dist/hosts/integration/pi-bootstrap-extension.d.ts +36 -0
- package/dist/hosts/integration/pi-bootstrap-extension.js +101 -0
- package/dist/hosts/integration/pi-bootstrap-guard.d.ts +125 -0
- package/dist/hosts/integration/pi-bootstrap-guard.js +438 -0
- package/dist/hosts/integration/pi-bootstrap-runtime.d.ts +63 -0
- package/dist/hosts/integration/pi-bootstrap-runtime.js +223 -0
- package/dist/hosts/integration/pi-native-events.d.ts +112 -0
- package/dist/hosts/integration/pi-native-events.js +348 -0
- package/dist/hosts/integration/pi-rollover-extension.d.ts +166 -0
- package/dist/hosts/integration/pi-rollover-extension.js +431 -0
- package/dist/hosts/integration/recovery-config.d.ts +48 -0
- package/dist/hosts/integration/recovery-config.js +117 -0
- package/dist/hosts/integration/service.d.ts +6 -1
- package/dist/hosts/integration/service.js +485 -35
- package/dist/hosts/integration/support.d.ts +7 -0
- package/dist/hosts/integration/support.js +9 -1
- package/dist/hosts/integration/templates.d.ts +3 -0
- package/dist/hosts/integration/templates.js +26 -3
- package/dist/hosts/pi/managed-host-identity.d.ts +2 -0
- package/dist/hosts/pi/managed-host-identity.js +40 -0
- package/dist/hosts/pi/managed-provider-diagnostics.d.ts +6 -0
- package/dist/hosts/pi/managed-provider-diagnostics.js +184 -0
- package/dist/hosts/pi/managed-sdk-session.d.ts +335 -0
- package/dist/hosts/pi/managed-sdk-session.js +1639 -0
- package/dist/hosts/pi/managed-session-extension.d.ts +20 -0
- package/dist/hosts/pi/managed-session-extension.js +84 -0
- package/dist/hosts/pi/managed-session.d.ts +177 -0
- package/dist/hosts/pi/managed-session.js +1557 -0
- package/dist/hosts/pi/protocol.js +1 -1
- package/dist/hosts/pi/query-result.js +1 -1
- package/dist/hosts/pi/resume-capability.d.ts +41 -0
- package/dist/hosts/pi/resume-capability.js +33 -0
- package/dist/hosts/pi/rpc.d.ts +182 -134
- package/dist/hosts/pi/rpc.js +230 -37
- package/dist/hosts/pilot.d.ts +97 -74
- package/dist/hosts/pilot.js +5 -1
- package/dist/hosts/version.d.ts +3 -0
- package/dist/hosts/version.js +18 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/infra/bounded-file.d.ts +19 -0
- package/dist/infra/bounded-file.js +66 -0
- package/dist/infra/fs.js +10 -2
- package/dist/infra/git.d.ts +29 -0
- package/dist/infra/git.js +45 -9
- package/dist/infra/json-property.d.ts +2 -0
- package/dist/infra/json-property.js +69 -0
- package/dist/infra/json.d.ts +4 -0
- package/dist/infra/json.js +24 -0
- package/dist/infra/owned-process-group.js +1 -1
- package/dist/infra/private-lock.d.ts +16 -0
- package/dist/infra/private-lock.js +148 -0
- package/dist/infra/private-state.d.ts +7 -0
- package/dist/infra/private-state.js +146 -0
- package/dist/infra/process-identity.d.ts +12 -0
- package/dist/infra/process-identity.js +142 -0
- package/dist/infra/validation.d.ts +5 -0
- package/dist/infra/validation.js +15 -0
- package/dist/init.d.ts +1 -0
- package/dist/init.js +6 -0
- package/dist/install/apply.d.ts +9 -1
- package/dist/install/apply.js +176 -12
- package/dist/install/dependencies.d.ts +38 -0
- package/dist/install/dependencies.js +282 -0
- package/dist/install/ownership.js +30 -1
- package/dist/install/references.d.ts +31 -0
- package/dist/install/references.js +208 -0
- package/dist/install/repair.d.ts +37 -0
- package/dist/install/repair.js +798 -0
- package/dist/install/reset-local.d.ts +16 -0
- package/dist/install/reset-local.js +304 -0
- package/dist/install/rollback.d.ts +3 -0
- package/dist/install/rollback.js +9 -3
- package/dist/install/runtime.d.ts +10 -6
- package/dist/install/runtime.js +299 -59
- package/dist/install/source-fingerprint.d.ts +2 -0
- package/dist/install/source-fingerprint.js +103 -0
- package/dist/knowledge/catalog.d.ts +1 -0
- package/dist/knowledge/catalog.js +143 -10
- package/dist/knowledge/journal.js +17 -7
- package/dist/knowledge/paths.js +2 -1
- package/dist/knowledge/refs.js +4 -0
- package/dist/knowledge/register-batch.d.ts +4 -0
- package/dist/knowledge/register-batch.js +225 -0
- package/dist/knowledge/relocate.d.ts +5 -0
- package/dist/knowledge/relocate.js +92 -0
- package/dist/knowledge/scan.js +103 -47
- package/dist/knowledge/service.d.ts +3 -0
- package/dist/knowledge/service.js +33 -1
- package/dist/knowledge/write.d.ts +4 -1
- package/dist/knowledge/write.js +1 -1
- package/dist/mcp/server.d.ts +4 -2
- package/dist/mcp/server.js +87 -136
- package/dist/schemas/collaboration-state-validation.d.ts +6 -0
- package/dist/schemas/collaboration-state-validation.js +342 -0
- package/dist/schemas/collaboration-state.d.ts +436 -0
- package/dist/schemas/collaboration-state.js +1 -0
- package/dist/schemas/collaboration.d.ts +335 -0
- package/dist/schemas/collaboration.js +272 -0
- package/dist/schemas/continuity.d.ts +8 -0
- package/dist/schemas/continuity.js +13 -3
- package/dist/schemas/doctor.d.ts +1 -0
- package/dist/schemas/doctor.js +1 -0
- package/dist/schemas/document.d.ts +61 -0
- package/dist/schemas/document.js +9 -0
- package/dist/schemas/errors.d.ts +1 -0
- package/dist/schemas/errors.js +1 -0
- package/dist/schemas/host.d.ts +24 -0
- package/dist/schemas/host.js +19 -0
- package/dist/schemas/install.d.ts +8 -2
- package/dist/schemas/install.js +11 -1
- package/dist/schemas/managed-pi-execution.d.ts +16 -0
- package/dist/schemas/managed-pi-execution.js +2 -0
- package/dist/schemas/outcome.d.ts +9 -5
- package/dist/workspace/config-file.js +1 -3
- package/dist/workspace/documents.js +12 -2
- package/dist/workspace/repositories.d.ts +7 -0
- package/dist/workspace/repositories.js +14 -5
- package/package.json +35 -35
- package/skills/pappra/SKILL.md +28 -7
- package/skills/pappra/references/collaboration.md +633 -0
- package/skills/pappra/references/environment.md +360 -0
- package/skills/pappra/references/hosts.md +20 -0
- package/skills/pappra/references/operations.md +36 -5
- package/dist/app/collab-from.d.ts +0 -35
- package/dist/app/collab-from.js +0 -84
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import { object as isRecord } from '../infra/validation.js';
|
|
1
2
|
import crypto from 'node:crypto';
|
|
2
3
|
import fs from 'node:fs';
|
|
3
4
|
import path from 'node:path';
|
|
4
5
|
import { collaborationCommandSchema, collaborationGoalSchema, collaborationPreparedSchema, } from '../schemas/collaboration.js';
|
|
5
|
-
import { admitLaunch, reserveLaunch, settleLaunch, } from '../collab/admission.js';
|
|
6
|
+
import { admitLaunch, reserveLaunch, settleLaunch, runnerForHarness, } from '../collab/admission.js';
|
|
6
7
|
import { buildMessage, sha256File } from '../collab/envelope.js';
|
|
7
8
|
import { findAttempt } from '../collab/lifecycle.js';
|
|
8
9
|
import { submitMessage } from '../collab/messages.js';
|
|
10
|
+
import { readMessageFromTaskRoot } from './collab-messages.js';
|
|
9
11
|
import { claimResources, releaseClaim } from '../collab/resources.js';
|
|
10
12
|
import { initTask, nowIso, readRecord, taskRootFor } from '../collab/store.js';
|
|
11
13
|
import { observePath, resolveRealpath } from '../infra/fs.js';
|
|
@@ -303,9 +305,6 @@ function publishCurrentResult(namespace, file, bytes) {
|
|
|
303
305
|
};
|
|
304
306
|
}
|
|
305
307
|
}
|
|
306
|
-
function isRecord(value) {
|
|
307
|
-
return typeof value === 'object' && value !== null;
|
|
308
|
-
}
|
|
309
308
|
function assistantRecord(record) {
|
|
310
309
|
if (!isRecord(record))
|
|
311
310
|
return null;
|
|
@@ -556,8 +555,13 @@ export async function prepareCollaboration(input, options) {
|
|
|
556
555
|
if (!path.isAbsolute(options.domainRoot)) {
|
|
557
556
|
return failure('invalid', 'domain_root_required', 'Domain root must be absolute.');
|
|
558
557
|
}
|
|
559
|
-
|
|
560
|
-
|
|
558
|
+
const runnerName = runnerForHarness(goal.assignee.harness);
|
|
559
|
+
if (runnerName === null) {
|
|
560
|
+
return failure('invalid', 'assignee_harness_unsupported', `Assignee harness ${goal.assignee.harness} has no supported runner; the supported harnesses are pi, codex and claude.`);
|
|
561
|
+
}
|
|
562
|
+
const assigneeCwd = await observePath(goal.assignee.cwd).catch(() => null);
|
|
563
|
+
if (assigneeCwd?.kind !== 'directory') {
|
|
564
|
+
return failure('needs_input', 'cwd_unavailable', 'Assignee cwd is not a real existing directory.');
|
|
561
565
|
}
|
|
562
566
|
let taskRoot;
|
|
563
567
|
let domainRoot;
|
|
@@ -626,6 +630,7 @@ export async function prepareCollaboration(input, options) {
|
|
|
626
630
|
for (const claimId of claimIds) {
|
|
627
631
|
releaseClaim(taskRoot, {
|
|
628
632
|
claimId,
|
|
633
|
+
holder: goal.assignee,
|
|
629
634
|
reason: 'prepare resource claim failed',
|
|
630
635
|
});
|
|
631
636
|
}
|
|
@@ -637,6 +642,7 @@ export async function prepareCollaboration(input, options) {
|
|
|
637
642
|
for (const claimId of claimIds) {
|
|
638
643
|
releaseClaim(taskRoot, {
|
|
639
644
|
claimId,
|
|
645
|
+
holder: goal.assignee,
|
|
640
646
|
reason: 'prepare resource epoch missing',
|
|
641
647
|
});
|
|
642
648
|
}
|
|
@@ -655,6 +661,7 @@ export async function prepareCollaboration(input, options) {
|
|
|
655
661
|
for (const claimId of claimIds) {
|
|
656
662
|
releaseClaim(taskRoot, {
|
|
657
663
|
claimId,
|
|
664
|
+
holder: goal.assignee,
|
|
658
665
|
reason: 'prepare failed before assignment',
|
|
659
666
|
});
|
|
660
667
|
}
|
|
@@ -667,6 +674,7 @@ export async function prepareCollaboration(input, options) {
|
|
|
667
674
|
for (const claimId of claimIds) {
|
|
668
675
|
releaseClaim(taskRoot, {
|
|
669
676
|
claimId,
|
|
677
|
+
holder: goal.assignee,
|
|
670
678
|
reason: 'prepare failed before assignment',
|
|
671
679
|
});
|
|
672
680
|
}
|
|
@@ -682,7 +690,7 @@ export async function prepareCollaboration(input, options) {
|
|
|
682
690
|
attempt: goal.attempt,
|
|
683
691
|
objective: goal.objective,
|
|
684
692
|
acceptance: goal.acceptance,
|
|
685
|
-
roles:
|
|
693
|
+
roles: goal.roles,
|
|
686
694
|
coordinator: {
|
|
687
695
|
agent_id: goal.coordinator.agent_id,
|
|
688
696
|
session_id: goal.coordinator.session_id,
|
|
@@ -707,7 +715,7 @@ export async function prepareCollaboration(input, options) {
|
|
|
707
715
|
resource_epoch: resourceEpochs[item.path],
|
|
708
716
|
})),
|
|
709
717
|
runner: {
|
|
710
|
-
name:
|
|
718
|
+
name: runnerName,
|
|
711
719
|
mode: 'native',
|
|
712
720
|
omitted_capabilities: [],
|
|
713
721
|
},
|
|
@@ -723,6 +731,7 @@ export async function prepareCollaboration(input, options) {
|
|
|
723
731
|
for (const claimId of claimIds) {
|
|
724
732
|
releaseClaim(taskRoot, {
|
|
725
733
|
claimId,
|
|
734
|
+
holder: goal.assignee,
|
|
726
735
|
reason: 'prepare failed before assignment',
|
|
727
736
|
});
|
|
728
737
|
}
|
|
@@ -762,6 +771,7 @@ export async function prepareCollaboration(input, options) {
|
|
|
762
771
|
for (const claimId of claimIds) {
|
|
763
772
|
releaseClaim(taskRoot, {
|
|
764
773
|
claimId,
|
|
774
|
+
holder: goal.assignee,
|
|
765
775
|
reason: 'assignment registration failed',
|
|
766
776
|
});
|
|
767
777
|
}
|
|
@@ -808,6 +818,10 @@ export async function admitPrepared(preparedFile, capabilitiesFile, options = {}
|
|
|
808
818
|
if (!bundle.ok)
|
|
809
819
|
return bundle;
|
|
810
820
|
const { prepared, goal, assignment } = bundle.value;
|
|
821
|
+
if (typeof options.controllerHarness === 'string' &&
|
|
822
|
+
assignment.assignee.harness !== options.controllerHarness) {
|
|
823
|
+
return failure('invalid', 'controller_harness_mismatch', `This controller launches ${options.controllerHarness} assignments only; the prepared assignment names ${assignment.assignee.harness}.`, 'Prepare the goal for the controller-supported harness or choose the matching controller.');
|
|
824
|
+
}
|
|
811
825
|
const card = (() => {
|
|
812
826
|
try {
|
|
813
827
|
return JSON.parse(fs.readFileSync(capabilitiesFile, 'utf8'));
|
|
@@ -846,10 +860,13 @@ export async function admitPrepared(preparedFile, capabilitiesFile, options = {}
|
|
|
846
860
|
value: { prepared, goal, assignment, admission, token },
|
|
847
861
|
};
|
|
848
862
|
}
|
|
863
|
+
/** Real controllers are bound to one harness; fixtures are not and stay explicit fixtures. */
|
|
864
|
+
const CONTROLLER_HARNESS = { 'pi-rpc': 'pi' };
|
|
849
865
|
export async function startPrepared(preparedFile, options) {
|
|
850
866
|
const clock = options.clock ?? Date.now();
|
|
851
867
|
const admitted = await admitPrepared(preparedFile, options.capabilitiesFile, {
|
|
852
868
|
clock,
|
|
869
|
+
controllerHarness: CONTROLLER_HARNESS[options.controller.id] ?? null,
|
|
853
870
|
});
|
|
854
871
|
if (!admitted.ok)
|
|
855
872
|
return admitted;
|
|
@@ -862,7 +879,7 @@ export async function startPrepared(preparedFile, options) {
|
|
|
862
879
|
return captured.failure;
|
|
863
880
|
let token = admitted.value.token;
|
|
864
881
|
let reservationId = null;
|
|
865
|
-
if (options.
|
|
882
|
+
if (options.controller.id === 'fixture') {
|
|
866
883
|
const reservation = reserveLaunch(prepared.task_root, {
|
|
867
884
|
expectRevision: prepared.state_revision,
|
|
868
885
|
clock,
|
|
@@ -1197,8 +1214,68 @@ export async function driveRun(started, commands, emit, { clock = Date.now() } =
|
|
|
1197
1214
|
});
|
|
1198
1215
|
continue;
|
|
1199
1216
|
}
|
|
1217
|
+
const intentContentDigest = intent.content_digest;
|
|
1218
|
+
if (intentContentDigest === null) {
|
|
1219
|
+
send('cancel', command.request_id, 'cancel-intent-unverified', 'The registered CANCEL has no content digest; native cancellation was not called.', {
|
|
1220
|
+
intent: true,
|
|
1221
|
+
native: {
|
|
1222
|
+
called: false,
|
|
1223
|
+
reason: 'cancel_intent_digest_missing',
|
|
1224
|
+
},
|
|
1225
|
+
});
|
|
1226
|
+
continue;
|
|
1227
|
+
}
|
|
1228
|
+
const registered = await readMessageFromTaskRoot(started.prepared.task_root, intent.message_id);
|
|
1229
|
+
const registeredMessage = registered.ok
|
|
1230
|
+
? registered.value.message
|
|
1231
|
+
: null;
|
|
1232
|
+
const entry = registered.ok ? registered.value.entry : null;
|
|
1233
|
+
const sameIdentity = (actual, expected) => actual !== null &&
|
|
1234
|
+
actual !== undefined &&
|
|
1235
|
+
actual.agent_id === expected.agent_id &&
|
|
1236
|
+
actual.session_id === expected.session_id &&
|
|
1237
|
+
(actual.harness ?? null) === (expected.harness ?? null);
|
|
1238
|
+
const intentApplied = registeredMessage !== null &&
|
|
1239
|
+
entry !== null &&
|
|
1240
|
+
intentContentDigest !== null &&
|
|
1241
|
+
registeredMessage.message_id === intent.message_id &&
|
|
1242
|
+
registeredMessage.type === 'CANCEL' &&
|
|
1243
|
+
registeredMessage.task_id === started.prepared.task_id &&
|
|
1244
|
+
registeredMessage.subtask_id === started.prepared.subtask_id &&
|
|
1245
|
+
registeredMessage.attempt === started.prepared.attempt &&
|
|
1246
|
+
registeredMessage.owner_epoch ===
|
|
1247
|
+
started.goal.coordinator.owner_epoch &&
|
|
1248
|
+
registeredMessage.content_digest === intentContentDigest &&
|
|
1249
|
+
sameIdentity(registeredMessage.from, started.goal.coordinator) &&
|
|
1250
|
+
sameIdentity(registeredMessage.to, started.assignment.assignee) &&
|
|
1251
|
+
entry.message_id === intent.message_id &&
|
|
1252
|
+
entry.type === 'CANCEL' &&
|
|
1253
|
+
entry.state === 'applied' &&
|
|
1254
|
+
entry.subtask_id === started.prepared.subtask_id &&
|
|
1255
|
+
entry.attempt === started.prepared.attempt &&
|
|
1256
|
+
entry.owner_epoch === started.goal.coordinator.owner_epoch &&
|
|
1257
|
+
entry.content_digest === intentContentDigest &&
|
|
1258
|
+
sameIdentity(entry.from, started.goal.coordinator) &&
|
|
1259
|
+
sameIdentity(entry.to, started.assignment.assignee);
|
|
1260
|
+
if (!registered.ok || !intentApplied) {
|
|
1261
|
+
send('cancel', command.request_id, 'cancel-intent-unverified', 'The registered CANCEL could not be verified as the applied intent for this run; native cancellation was not called.', {
|
|
1262
|
+
intent: true,
|
|
1263
|
+
native: {
|
|
1264
|
+
called: false,
|
|
1265
|
+
reason: registered.ok
|
|
1266
|
+
? 'cancel_intent_not_applied'
|
|
1267
|
+
: registered.code,
|
|
1268
|
+
},
|
|
1269
|
+
});
|
|
1270
|
+
continue;
|
|
1271
|
+
}
|
|
1272
|
+
const cancellationIntent = {
|
|
1273
|
+
messageId: intent.message_id,
|
|
1274
|
+
contentDigest: intentContentDigest,
|
|
1275
|
+
fileDigest: registered.value.digest,
|
|
1276
|
+
};
|
|
1200
1277
|
try {
|
|
1201
|
-
const cancel = await started.session.cancel();
|
|
1278
|
+
const cancel = await started.session.cancel(cancellationIntent);
|
|
1202
1279
|
send('cancel', command.request_id, 'cancel-requested', null, {
|
|
1203
1280
|
intent: true,
|
|
1204
1281
|
native: cancel,
|
|
@@ -1295,7 +1372,11 @@ export class FixtureController {
|
|
|
1295
1372
|
}
|
|
1296
1373
|
}
|
|
1297
1374
|
export class PiRpcController {
|
|
1375
|
+
options;
|
|
1298
1376
|
id = 'pi-rpc';
|
|
1377
|
+
constructor(options = {}) {
|
|
1378
|
+
this.options = options;
|
|
1379
|
+
}
|
|
1299
1380
|
async start(token, request) {
|
|
1300
1381
|
if (!validateLaunchToken(token, {
|
|
1301
1382
|
taskRoot: request.taskRoot,
|
|
@@ -1312,6 +1393,7 @@ export class PiRpcController {
|
|
|
1312
1393
|
};
|
|
1313
1394
|
}
|
|
1314
1395
|
const started = await startOwnedPiRpc({
|
|
1396
|
+
...this.options,
|
|
1315
1397
|
taskRoot: request.taskRoot,
|
|
1316
1398
|
cwd: request.assignment.assignee.cwd,
|
|
1317
1399
|
assignmentFile: request.prepared.assignment_path,
|
|
@@ -1416,7 +1498,7 @@ export class PiRpcController {
|
|
|
1416
1498
|
};
|
|
1417
1499
|
}
|
|
1418
1500
|
},
|
|
1419
|
-
cancel: async () => {
|
|
1501
|
+
cancel: async (intent) => {
|
|
1420
1502
|
const namespaceProblem = liveNamespaceProblem();
|
|
1421
1503
|
if (namespaceProblem !== null) {
|
|
1422
1504
|
return {
|
|
@@ -1428,7 +1510,7 @@ export class PiRpcController {
|
|
|
1428
1510
|
},
|
|
1429
1511
|
};
|
|
1430
1512
|
}
|
|
1431
|
-
const cancel = await started.session.cancel();
|
|
1513
|
+
const cancel = await started.session.cancel(intent === undefined ? undefined : { intent });
|
|
1432
1514
|
const entry = cancel !== null && typeof cancel === 'object'
|
|
1433
1515
|
? cancel
|
|
1434
1516
|
: {};
|
|
@@ -1641,3 +1723,10 @@ export function inspectPreparedHistory(preparedFile) {
|
|
|
1641
1723
|
},
|
|
1642
1724
|
};
|
|
1643
1725
|
}
|
|
1726
|
+
// Read-only work-plan inspection is exposed alongside the collaboration app entry so an
|
|
1727
|
+
// installed consumer can evaluate a persisted plan without a second assignment registry.
|
|
1728
|
+
export { COLLAB_WORK_PLAN_REPORT_SCHEMA, inspectWorkPlan, } from './collab-work-plan.js';
|
|
1729
|
+
// Read-only rollover inspection is exposed alongside the collaboration app entry so an
|
|
1730
|
+
// installed consumer can inspect the exact record and linked bundle. There is no apply
|
|
1731
|
+
// path here: caller JSON is never accepted as native stop proof.
|
|
1732
|
+
export { COLLAB_ROLLOVER_REPORT_SCHEMA, COLLAB_ROLLOVER_REQUEST_SCHEMA, inspectOrPlanRollover, loadRolloverRequest, } from './collab-rollover.js';
|
package/dist/app/doctor.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { join } from 'node:path';
|
|
1
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { diagnosticCodes } from '../schemas/errors.js';
|
|
4
4
|
import { DOCTOR_REPORT_SCHEMA, doctorReportSchema, } from '../schemas/doctor.js';
|
|
@@ -10,6 +10,9 @@ import { resolveWorkspaceRoot } from '../workspace/resolve.js';
|
|
|
10
10
|
import { resolveUserDataDir, inspectRuntimeStatePassive, } from '../install/runtime.js';
|
|
11
11
|
import { ensureStatePathBoundary, inspectOwnedFile, readInstallJournal, readWorkspaceManifest, rootIdentityOf, scanJournalDirectory, workspaceInstallPaths, } from '../install/ownership.js';
|
|
12
12
|
import { detectPassiveHosts, detectPassiveTool } from '../hosts/detect.js';
|
|
13
|
+
import { inspectWorkspaceLocks } from '../install/repair.js';
|
|
14
|
+
import { inspectLocalResetState } from '../install/reset-local.js';
|
|
15
|
+
import { inspectPrivateLock } from '../infra/private-lock.js';
|
|
13
16
|
function truncateText(value, limit) {
|
|
14
17
|
return value.length <= limit
|
|
15
18
|
? value
|
|
@@ -127,7 +130,9 @@ async function inspectInstallState(root, observedAt) {
|
|
|
127
130
|
actions.push(`Review ${entry.path}; rollback/uninstall will preserve the user-modified file.`);
|
|
128
131
|
}
|
|
129
132
|
if (entry.backupRef !== null) {
|
|
130
|
-
const backupPath =
|
|
133
|
+
const backupPath = isAbsolute(entry.backupRef)
|
|
134
|
+
? resolve(entry.backupRef)
|
|
135
|
+
: join(root, ...entry.backupRef.split('/'));
|
|
131
136
|
const backupBoundary = await ensureStatePathBoundary(root, backupPath);
|
|
132
137
|
const realBackup = await resolveRealpath(backupPath);
|
|
133
138
|
const backupEscapes = realBackup !== null &&
|
|
@@ -247,6 +252,19 @@ function runtimeCheck(targetVersion, machineRuntime, passive, machinePath, obser
|
|
|
247
252
|
nextActions: ['Run pappra init to establish a workspace runtime target.'],
|
|
248
253
|
});
|
|
249
254
|
}
|
|
255
|
+
if (passive?.status === 'incompatible') {
|
|
256
|
+
return makeCheck({
|
|
257
|
+
id: 'runtime.state',
|
|
258
|
+
code: diagnosticCodes.runtimeStateIncompatible,
|
|
259
|
+
status: 'error',
|
|
260
|
+
severity: 'error',
|
|
261
|
+
title: 'Runtime state requires reinitialization',
|
|
262
|
+
summary: diagnosticsSummary(passive.diagnostics),
|
|
263
|
+
source: passive.runtimeRoot,
|
|
264
|
+
observedAt,
|
|
265
|
+
nextActions: passive.diagnostics.flatMap((entry) => entry.hint === null ? [] : [entry.hint]),
|
|
266
|
+
});
|
|
267
|
+
}
|
|
250
268
|
if (machineRuntime === null) {
|
|
251
269
|
if (passive === null) {
|
|
252
270
|
return makeCheck({
|
|
@@ -663,6 +681,63 @@ export async function runDoctor(options = {}) {
|
|
|
663
681
|
],
|
|
664
682
|
}));
|
|
665
683
|
if (root !== null) {
|
|
684
|
+
const resetState = await inspectLocalResetState(root);
|
|
685
|
+
if (resetState === 'pending' || resetState === 'unreadable')
|
|
686
|
+
checks.push(makeCheck({
|
|
687
|
+
id: 'state.local-reset',
|
|
688
|
+
code: diagnosticCodes.initApplyResidual,
|
|
689
|
+
status: 'warning',
|
|
690
|
+
severity: 'warning',
|
|
691
|
+
title: 'Private local reset',
|
|
692
|
+
summary: `Local reset metadata is ${resetState}; retained archives were not changed.`,
|
|
693
|
+
source: join(root, '.pappra/local-archive/pending-reset.json'),
|
|
694
|
+
observedAt,
|
|
695
|
+
coverage: 'read-only local reset journal status; no process is stopped or adopted',
|
|
696
|
+
evidence: [],
|
|
697
|
+
nextActions: [
|
|
698
|
+
'After stopping all affected writers, resume with pappra repair --reset-local --all-writers-stopped --apply.',
|
|
699
|
+
],
|
|
700
|
+
}));
|
|
701
|
+
try {
|
|
702
|
+
const locks = await inspectWorkspaceLocks(root);
|
|
703
|
+
const needsRepair = locks.some((lock) => lock.state === 'dead' || lock.state === 'unknown');
|
|
704
|
+
checks.push(makeCheck({
|
|
705
|
+
id: 'state.writer-locks',
|
|
706
|
+
code: diagnosticCodes.document_locked,
|
|
707
|
+
status: needsRepair ? 'warning' : 'ok',
|
|
708
|
+
severity: needsRepair ? 'warning' : 'info',
|
|
709
|
+
title: 'Private writer locks',
|
|
710
|
+
summary: locks.length === 0
|
|
711
|
+
? 'No private writer lock remains.'
|
|
712
|
+
: locks.map((lock) => `${lock.state}: ${lock.file}`).join('; '),
|
|
713
|
+
source: join(root, '.pappra/local'),
|
|
714
|
+
observedAt,
|
|
715
|
+
coverage: 'bounded install, knowledge and checkpoint lock inventory; process identity or positive owner-death evidence',
|
|
716
|
+
evidence: locks.map((lock) => fileEvidence(lock.file, observedAt, lock.digest)),
|
|
717
|
+
nextActions: needsRepair
|
|
718
|
+
? [
|
|
719
|
+
'Run pappra repair to inspect owner states; use --apply only for provably dead writers. Unknown/live owners are preserved.',
|
|
720
|
+
]
|
|
721
|
+
: [],
|
|
722
|
+
}));
|
|
723
|
+
}
|
|
724
|
+
catch (error) {
|
|
725
|
+
checks.push(makeCheck({
|
|
726
|
+
id: 'state.writer-locks',
|
|
727
|
+
code: diagnosticCodes.document_locked,
|
|
728
|
+
status: 'error',
|
|
729
|
+
severity: 'error',
|
|
730
|
+
title: 'Private writer locks',
|
|
731
|
+
summary: `Cannot inspect private lock state: ${error instanceof Error ? error.message : String(error)}`,
|
|
732
|
+
source: join(root, '.pappra/local'),
|
|
733
|
+
observedAt,
|
|
734
|
+
coverage: 'private lock paths are unsafe or inventory was unavailable',
|
|
735
|
+
evidence: [],
|
|
736
|
+
nextActions: [
|
|
737
|
+
'Inspect pappra repair output and the reported path; no lock was changed.',
|
|
738
|
+
],
|
|
739
|
+
}));
|
|
740
|
+
}
|
|
666
741
|
const workspaceInspection = await inspectWorkspaceConfig(root, {
|
|
667
742
|
missing: 'info',
|
|
668
743
|
});
|
|
@@ -886,6 +961,7 @@ export async function runDoctor(options = {}) {
|
|
|
886
961
|
],
|
|
887
962
|
}));
|
|
888
963
|
const selectedHosts = machine?.selectedHosts ?? [];
|
|
964
|
+
const observableHosts = ['pi', 'codex', 'claude'];
|
|
889
965
|
// Passive observation only: a host install can target the private default root, a
|
|
890
966
|
// project directory, or Codex's user configuration directory.
|
|
891
967
|
const hostInstallRoots = (host) => {
|
|
@@ -898,7 +974,7 @@ export async function runDoctor(options = {}) {
|
|
|
898
974
|
}
|
|
899
975
|
return roots;
|
|
900
976
|
};
|
|
901
|
-
const installedHostBundle = (await Promise.all(
|
|
977
|
+
const installedHostBundle = (await Promise.all(observableHosts.map(async (host) => {
|
|
902
978
|
for (const base of hostInstallRoots(host)) {
|
|
903
979
|
const path = join(base, 'pappra-host.json');
|
|
904
980
|
if ((await observePath(path)).kind === 'file')
|
|
@@ -906,7 +982,34 @@ export async function runDoctor(options = {}) {
|
|
|
906
982
|
}
|
|
907
983
|
return null;
|
|
908
984
|
}))).find((path) => path !== null) ?? null;
|
|
909
|
-
const
|
|
985
|
+
const hostLocks = (await Promise.all(observableHosts.flatMap((host) => hostInstallRoots(host).map(async (base) => ({
|
|
986
|
+
host,
|
|
987
|
+
base,
|
|
988
|
+
owner: await inspectPrivateLock(join(base, '.pappra-host.lock')),
|
|
989
|
+
}))))).filter((entry) => entry.owner.state !== 'missing');
|
|
990
|
+
if (hostLocks.length > 0)
|
|
991
|
+
checks.push(makeCheck({
|
|
992
|
+
id: 'host.install-locks',
|
|
993
|
+
code: diagnosticCodes.host_install_conflict,
|
|
994
|
+
status: hostLocks.every((entry) => entry.owner.state === 'alive')
|
|
995
|
+
? 'partial'
|
|
996
|
+
: 'warning',
|
|
997
|
+
severity: hostLocks.every((entry) => entry.owner.state === 'alive')
|
|
998
|
+
? 'info'
|
|
999
|
+
: 'warning',
|
|
1000
|
+
title: 'Host installation locks',
|
|
1001
|
+
summary: `${hostLocks.length} host lock(s) observed; live owners are retained, dead owners can be reclaimed, and unknown owners require an explicit stopped-writer assertion.`,
|
|
1002
|
+
source: hostLocks[0].owner.file,
|
|
1003
|
+
observedAt,
|
|
1004
|
+
coverage: 'known project/private/user host roots only',
|
|
1005
|
+
evidence: hostLocks.map((entry) => fileEvidence(entry.owner.file, observedAt, entry.owner.digest)),
|
|
1006
|
+
nextActions: hostLocks.map((entry) => entry.owner.state === 'alive'
|
|
1007
|
+
? `Let the verified live ${entry.host} writer finish; --force cannot override it.`
|
|
1008
|
+
: entry.owner.state === 'dead'
|
|
1009
|
+
? `Retry host install --host ${entry.host} --host-root ${JSON.stringify(entry.base)} to reclaim the proven dead owner.`
|
|
1010
|
+
: `Stop all writers for ${entry.base}, then run pappra host uninstall --host ${entry.host} --host-root ${JSON.stringify(entry.base)} --force --apply to archive an unknown lock.`),
|
|
1011
|
+
}));
|
|
1012
|
+
const installedHostSkill = (await Promise.all(observableHosts.map(async (host) => {
|
|
910
1013
|
for (const base of hostInstallRoots(host)) {
|
|
911
1014
|
const path = join(base, 'skills', 'pappra', 'SKILL.md');
|
|
912
1015
|
if ((await observePath(path)).kind === 'file')
|
|
@@ -922,7 +1025,7 @@ export async function runDoctor(options = {}) {
|
|
|
922
1025
|
severity: 'info',
|
|
923
1026
|
title: 'Host integration',
|
|
924
1027
|
summary: installedHostBundle === null
|
|
925
|
-
? 'No
|
|
1028
|
+
? 'No host bundle manifest was observed under the known host roots; no host, session, or model was started.'
|
|
926
1029
|
: `P09 host bundle manifest exists at ${installedHostBundle}; native trust, reload and callability remain unverified and no host was started.`,
|
|
927
1030
|
source: installedHostBundle ?? root ?? homeDir,
|
|
928
1031
|
observedAt,
|
package/dist/app/host-mcp.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
2
|
import { dirname } from 'node:path';
|
|
3
|
+
import { observePath, readTextFileBounded } from '../infra/fs.js';
|
|
4
|
+
import { ensureStatePathBoundary, writeJsonAtomic, } from '../install/ownership.js';
|
|
4
5
|
const MCP_ENTRY = { command: 'pappra', args: ['mcp'] };
|
|
5
6
|
function mcpConfigPath(host, root) {
|
|
6
7
|
if (host === 'claude')
|
|
@@ -19,10 +20,26 @@ function sameEntry(value) {
|
|
|
19
20
|
args.length === MCP_ENTRY.args.length &&
|
|
20
21
|
args.every((arg, index) => arg === MCP_ENTRY.args[index]));
|
|
21
22
|
}
|
|
22
|
-
async function readConfig(path) {
|
|
23
|
+
async function readConfig(path, root) {
|
|
23
24
|
let text;
|
|
24
25
|
try {
|
|
25
|
-
|
|
26
|
+
const boundary = await ensureStatePathBoundary(root, path);
|
|
27
|
+
if (!boundary.ok)
|
|
28
|
+
return { ok: false, message: boundary.message };
|
|
29
|
+
const observation = await observePath(path);
|
|
30
|
+
if (observation.kind === 'missing') {
|
|
31
|
+
return { ok: true, value: {}, exists: false };
|
|
32
|
+
}
|
|
33
|
+
if (observation.kind !== 'file') {
|
|
34
|
+
return {
|
|
35
|
+
ok: false,
|
|
36
|
+
message: `MCP config is not a regular file: ${path}`,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const read = await readTextFileBounded(path);
|
|
40
|
+
if (!read.ok)
|
|
41
|
+
return { ok: false, message: read.message };
|
|
42
|
+
text = read.value.text;
|
|
26
43
|
}
|
|
27
44
|
catch (error) {
|
|
28
45
|
if (typeof error === 'object' &&
|
|
@@ -72,7 +89,7 @@ export async function planHostMcp(host, root) {
|
|
|
72
89
|
message: `No MCP registration is defined for ${host}.`,
|
|
73
90
|
};
|
|
74
91
|
}
|
|
75
|
-
const config = await readConfig(target);
|
|
92
|
+
const config = await readConfig(target, root);
|
|
76
93
|
if (!config.ok) {
|
|
77
94
|
return {
|
|
78
95
|
host,
|
|
@@ -150,7 +167,8 @@ export async function applyHostMcp(plan, options) {
|
|
|
150
167
|
if (target === null) {
|
|
151
168
|
return { ...plan, applied: false, verified: false, detail: plan.message };
|
|
152
169
|
}
|
|
153
|
-
const
|
|
170
|
+
const root = plan.host === 'pi' ? dirname(dirname(target)) : dirname(target);
|
|
171
|
+
const config = await readConfig(target, root);
|
|
154
172
|
if (!config.ok) {
|
|
155
173
|
return { ...plan, applied: false, verified: false, detail: config.message };
|
|
156
174
|
}
|
|
@@ -161,8 +179,15 @@ export async function applyHostMcp(plan, options) {
|
|
|
161
179
|
: {}),
|
|
162
180
|
pappra: MCP_ENTRY,
|
|
163
181
|
};
|
|
164
|
-
await
|
|
165
|
-
|
|
182
|
+
const written = await writeJsonAtomic(target, { ...config.value, mcpServers: nextServers }, root);
|
|
183
|
+
if (!written.ok) {
|
|
184
|
+
return {
|
|
185
|
+
...plan,
|
|
186
|
+
applied: false,
|
|
187
|
+
verified: false,
|
|
188
|
+
detail: written.message,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
166
191
|
const verified = await verifyHostMcp(plan);
|
|
167
192
|
return {
|
|
168
193
|
...plan,
|
|
@@ -210,7 +235,8 @@ export async function verifyHostMcp(plan) {
|
|
|
210
235
|
if (plan.target === null) {
|
|
211
236
|
return { ...plan, applied: false, verified: false, detail: plan.message };
|
|
212
237
|
}
|
|
213
|
-
const
|
|
238
|
+
const root = plan.host === 'pi' ? dirname(dirname(plan.target)) : dirname(plan.target);
|
|
239
|
+
const config = await readConfig(plan.target, root);
|
|
214
240
|
if (!config.ok) {
|
|
215
241
|
return { ...plan, applied: false, verified: false, detail: config.message };
|
|
216
242
|
}
|
package/dist/app/host-probe.js
CHANGED
|
@@ -3,7 +3,7 @@ import { mkdtempSync, rmSync } from 'node:fs';
|
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
|
-
import { HOST_SUPPORT } from '../hosts/integration/support.js';
|
|
6
|
+
import { HOST_SUPPORT, isSupportedVersion, } from '../hosts/integration/support.js';
|
|
7
7
|
function commandVersion(command) {
|
|
8
8
|
const result = spawnSync(command, ['--version'], {
|
|
9
9
|
encoding: 'utf8',
|
|
@@ -42,10 +42,10 @@ export function probeHost(host) {
|
|
|
42
42
|
version,
|
|
43
43
|
pinnedVersion,
|
|
44
44
|
protocol: 'version-only',
|
|
45
|
-
ok: version
|
|
45
|
+
ok: isSupportedVersion(host, version),
|
|
46
46
|
details: { matchesPinnedVersion: version === pinnedVersion },
|
|
47
|
-
message: version
|
|
48
|
-
? 'Version matches
|
|
47
|
+
message: isSupportedVersion(host, version)
|
|
48
|
+
? 'Version matches an observed event contract; lifecycle callability still needs a real session.'
|
|
49
49
|
: `Version ${version} does not match the pinned ${pinnedVersion}.`,
|
|
50
50
|
};
|
|
51
51
|
}
|
package/dist/app/init.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { DiagnosticCode } from '../schemas/errors.js';
|
|
|
3
3
|
import { type InitChoices, type InitExternalAction, type InitPlan } from '../schemas/init.js';
|
|
4
4
|
import { type MachineConfig } from '../schemas/machine.js';
|
|
5
5
|
import { type WorkspaceConfig, type WorkspaceValidation } from '../schemas/workspace.js';
|
|
6
|
+
import { type GitRepositoryObservation } from '../infra/git.js';
|
|
6
7
|
import type { MachineConfigInspection, WorkspaceConfigInspection } from '../workspace/config.js';
|
|
7
8
|
import { type WorkspaceDiscovery } from '../workspace/discover.js';
|
|
8
9
|
import { type PlannedFileContent } from '../install/plan.js';
|
|
@@ -96,6 +97,8 @@ export interface InitApplyResult {
|
|
|
96
97
|
export interface InitApplyOptions {
|
|
97
98
|
/** Fault-injection seam for tests; the CLI never supplies this hook. */
|
|
98
99
|
readonly beforeWrite?: (relativePath: string, index: number) => Promise<void> | void;
|
|
100
|
+
/** Test seam after durable create, before its identity is journaled; never supplied by CLI. */
|
|
101
|
+
readonly afterWrite?: (relativePath: string, index: number) => Promise<void> | void;
|
|
99
102
|
}
|
|
100
103
|
export interface KnowledgeCandidate {
|
|
101
104
|
readonly path: string;
|
|
@@ -113,6 +116,27 @@ export interface RepositoryCandidate {
|
|
|
113
116
|
readonly dirty: boolean | null;
|
|
114
117
|
readonly configured: boolean;
|
|
115
118
|
}
|
|
119
|
+
export type InitPathAdditionKind = 'repository' | 'knowledge';
|
|
120
|
+
/** Read-only snapshot of one exact, explicitly entered workspace-relative path. */
|
|
121
|
+
export interface InitPathObservation {
|
|
122
|
+
readonly kind: InitPathAdditionKind;
|
|
123
|
+
readonly requestedPath: string;
|
|
124
|
+
/** Canonical workspace-relative path suitable for an existing InitChoices field. */
|
|
125
|
+
readonly path: string;
|
|
126
|
+
readonly realPath: string;
|
|
127
|
+
readonly targetKind: 'file' | 'directory';
|
|
128
|
+
readonly device: number;
|
|
129
|
+
readonly inode: number;
|
|
130
|
+
/** Actual bounded Git metadata observation for repository additions. */
|
|
131
|
+
readonly git: GitRepositoryObservation | null;
|
|
132
|
+
}
|
|
133
|
+
export type InitPathInspectionResult = {
|
|
134
|
+
readonly ok: true;
|
|
135
|
+
readonly observation: InitPathObservation;
|
|
136
|
+
} | {
|
|
137
|
+
readonly ok: false;
|
|
138
|
+
readonly diagnostics: readonly Diagnostic[];
|
|
139
|
+
};
|
|
116
140
|
export interface PlanInitOptions {
|
|
117
141
|
readonly root: string;
|
|
118
142
|
readonly choices?: InitChoices | null;
|
|
@@ -123,6 +147,8 @@ export interface PlanInitOptions {
|
|
|
123
147
|
readonly discoveredAt?: string;
|
|
124
148
|
/** Reuse an immediately preceding inspectInitContext result. */
|
|
125
149
|
readonly context?: InitContext;
|
|
150
|
+
/** In-memory UI observations; planning re-inspects each path before use. */
|
|
151
|
+
readonly pathObservations?: readonly InitPathObservation[];
|
|
126
152
|
}
|
|
127
153
|
/** Resolves the explicit root and refuses to create a hidden nested workspace. */
|
|
128
154
|
export declare function resolveInitRoot(rootPath: string): Promise<InitRootResult>;
|
|
@@ -151,6 +177,32 @@ export interface DerivedInitChoices {
|
|
|
151
177
|
readonly rules: readonly InitChoiceRule[];
|
|
152
178
|
readonly candidates: InitChoiceCandidates;
|
|
153
179
|
}
|
|
180
|
+
export interface CommonInitDefaults {
|
|
181
|
+
readonly name: string;
|
|
182
|
+
readonly knowledge: InitChoices['knowledge'];
|
|
183
|
+
readonly code: Pick<InitChoices['code'], 'symbols' | 'prepare'>;
|
|
184
|
+
readonly excludes: readonly string[];
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Pure discovery-based selections shared by derived and interactive setup.
|
|
188
|
+
* Mode-specific repositories, validation, graph and host choices stay with
|
|
189
|
+
* their callers.
|
|
190
|
+
*/
|
|
191
|
+
export declare function deriveCommonInitDefaults(discovery: WorkspaceDiscovery): CommonInitDefaults;
|
|
192
|
+
/**
|
|
193
|
+
* Converts an existing valid workspace/machine pair into a lossless editable
|
|
194
|
+
* selection draft. Extensions and continuity are intentionally not part of
|
|
195
|
+
* InitChoices; plan reconstruction preserves them from the existing config.
|
|
196
|
+
*/
|
|
197
|
+
export declare function existingInitChoices(config: WorkspaceConfig, machine: MachineConfig | null): InitChoices;
|
|
198
|
+
export type InitEditableField = 'name' | 'newDocuments' | 'excludes' | 'repositoryPath' | 'knowledgePath';
|
|
199
|
+
/**
|
|
200
|
+
* Immediate UI field feedback using the same name schema, portable-path rules,
|
|
201
|
+
* literal-prefix exclusion policy and workspace-relative semantics as planning.
|
|
202
|
+
*/
|
|
203
|
+
export declare function validateInitField(field: InitEditableField, value: string): string | null;
|
|
204
|
+
/** Inspects one user-entered path only; it never broadens workspace discovery. */
|
|
205
|
+
export declare function inspectInitPathAddition(root: string, kind: InitPathAdditionKind, inputPath: string, excludes?: readonly string[], location?: string | null): Promise<InitPathInspectionResult>;
|
|
154
206
|
export declare function detectedHostVersion(command: string, pathEnv: string): string | null;
|
|
155
207
|
/**
|
|
156
208
|
* Derives deterministic init selections from discovery alone. Every value is
|
|
@@ -166,7 +218,7 @@ export declare function deriveInitChoices(discovery: WorkspaceDiscovery, options
|
|
|
166
218
|
*/
|
|
167
219
|
export declare function planWorkspaceInit(options: PlanInitOptions): Promise<InitPreparation>;
|
|
168
220
|
/**
|
|
169
|
-
* Applies
|
|
221
|
+
* Applies create and managed replacement operations from a plan, after revalidating root,
|
|
170
222
|
* configuration digest, target absence and exact bytes. External actions are
|
|
171
223
|
* reported as pending and never executed here.
|
|
172
224
|
*/
|
|
@@ -179,8 +231,10 @@ export interface RuntimeFlowResult {
|
|
|
179
231
|
}
|
|
180
232
|
export declare function bindAndRecordRuntime(root: string, installed: RuntimeInstallResult): Promise<RuntimeFlowResult>;
|
|
181
233
|
/**
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
234
|
+
* Ensures and binds the runtime package that is currently executing.
|
|
235
|
+
*
|
|
236
|
+
* The installer reuses an existing runtime only after its manifest, package
|
|
237
|
+
* contents, executable and smoke checks pass; a regular file is not proof of a
|
|
238
|
+
* verified durable runtime.
|
|
185
239
|
*/
|
|
186
240
|
export declare function ensureDefaultRuntime(root: string, targetVersion: string): Promise<RuntimeFlowResult>;
|