@caupulican/pi-adaptative 0.84.1 → 0.85.0

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 (145) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts.map +1 -1
  3. package/dist/bundled-resources/extensions/tmux-agent-manager/index.js +14 -8
  4. package/dist/bundled-resources/extensions/tmux-agent-manager/index.js.map +1 -1
  5. package/dist/bundled-resources/extensions/tmux-agent-manager/index.ts +14 -8
  6. package/dist/bundled-resources/skills/deduplicate-by-evidence/SKILL.md +7 -5
  7. package/dist/bundled-resources/skills/worker-profile-writer/SKILL.md +63 -0
  8. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
  9. package/dist/core/autonomy/lane-tool-surface.js +4 -2
  10. package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
  11. package/dist/core/background-lane-controller.d.ts +3 -0
  12. package/dist/core/background-lane-controller.d.ts.map +1 -1
  13. package/dist/core/background-lane-controller.js +6 -0
  14. package/dist/core/background-lane-controller.js.map +1 -1
  15. package/dist/core/default-tool-surface.d.ts.map +1 -1
  16. package/dist/core/default-tool-surface.js +1 -0
  17. package/dist/core/default-tool-surface.js.map +1 -1
  18. package/dist/core/delegation/worker-delegation-controller.d.ts +7 -0
  19. package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
  20. package/dist/core/delegation/worker-delegation-controller.js +28 -0
  21. package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
  22. package/dist/core/delegation/worker-profile-resolver.d.ts +2 -0
  23. package/dist/core/delegation/worker-profile-resolver.d.ts.map +1 -1
  24. package/dist/core/delegation/worker-profile-resolver.js +46 -11
  25. package/dist/core/delegation/worker-profile-resolver.js.map +1 -1
  26. package/dist/core/doctor.d.ts +1 -1
  27. package/dist/core/doctor.d.ts.map +1 -1
  28. package/dist/core/doctor.js +5 -4
  29. package/dist/core/doctor.js.map +1 -1
  30. package/dist/core/model-capability.d.ts.map +1 -1
  31. package/dist/core/model-capability.js +1 -0
  32. package/dist/core/model-capability.js.map +1 -1
  33. package/dist/core/orchestration/lane-tool-manifests.d.ts +1 -1
  34. package/dist/core/orchestration/lane-tool-manifests.d.ts.map +1 -1
  35. package/dist/core/orchestration/lane-tool-manifests.js +6 -1
  36. package/dist/core/orchestration/lane-tool-manifests.js.map +1 -1
  37. package/dist/core/orchestration/session-task-profile-store.d.ts +31 -0
  38. package/dist/core/orchestration/session-task-profile-store.d.ts.map +1 -0
  39. package/dist/core/orchestration/session-task-profile-store.js +138 -0
  40. package/dist/core/orchestration/session-task-profile-store.js.map +1 -0
  41. package/dist/core/orchestration/task-profile-writer.d.ts +58 -0
  42. package/dist/core/orchestration/task-profile-writer.d.ts.map +1 -0
  43. package/dist/core/orchestration/task-profile-writer.js +165 -0
  44. package/dist/core/orchestration/task-profile-writer.js.map +1 -0
  45. package/dist/core/runtime-builder.d.ts +3 -1
  46. package/dist/core/runtime-builder.d.ts.map +1 -1
  47. package/dist/core/runtime-builder.js +14 -0
  48. package/dist/core/runtime-builder.js.map +1 -1
  49. package/dist/core/security/untrusted-boundary.d.ts.map +1 -1
  50. package/dist/core/security/untrusted-boundary.js +4 -6
  51. package/dist/core/security/untrusted-boundary.js.map +1 -1
  52. package/dist/core/session-role.d.ts.map +1 -1
  53. package/dist/core/session-role.js +1 -0
  54. package/dist/core/session-role.js.map +1 -1
  55. package/dist/core/system-prompt.d.ts.map +1 -1
  56. package/dist/core/system-prompt.js +21 -23
  57. package/dist/core/system-prompt.js.map +1 -1
  58. package/dist/core/tool-capability-policy.js +1 -1
  59. package/dist/core/tool-capability-policy.js.map +1 -1
  60. package/dist/core/tools/bash.d.ts +1 -6
  61. package/dist/core/tools/bash.d.ts.map +1 -1
  62. package/dist/core/tools/bash.js +2 -26
  63. package/dist/core/tools/bash.js.map +1 -1
  64. package/dist/core/tools/delegate.js +4 -4
  65. package/dist/core/tools/delegate.js.map +1 -1
  66. package/dist/core/tools/edit-diff.d.ts +32 -5
  67. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  68. package/dist/core/tools/edit-diff.js +137 -69
  69. package/dist/core/tools/edit-diff.js.map +1 -1
  70. package/dist/core/tools/edit.d.ts +22 -7
  71. package/dist/core/tools/edit.d.ts.map +1 -1
  72. package/dist/core/tools/edit.js +128 -55
  73. package/dist/core/tools/edit.js.map +1 -1
  74. package/dist/core/tools/file-mutation-intent.d.ts +70 -0
  75. package/dist/core/tools/file-mutation-intent.d.ts.map +1 -0
  76. package/dist/core/tools/file-mutation-intent.js +325 -0
  77. package/dist/core/tools/file-mutation-intent.js.map +1 -0
  78. package/dist/core/tools/goal.d.ts.map +1 -1
  79. package/dist/core/tools/goal.js +41 -18
  80. package/dist/core/tools/goal.js.map +1 -1
  81. package/dist/core/tools/index.d.ts +3 -1
  82. package/dist/core/tools/index.d.ts.map +1 -1
  83. package/dist/core/tools/index.js +31 -8
  84. package/dist/core/tools/index.js.map +1 -1
  85. package/dist/core/tools/profile-writer.d.ts +33 -0
  86. package/dist/core/tools/profile-writer.d.ts.map +1 -0
  87. package/dist/core/tools/profile-writer.js +67 -0
  88. package/dist/core/tools/profile-writer.js.map +1 -0
  89. package/dist/core/tools/task-steps.d.ts.map +1 -1
  90. package/dist/core/tools/task-steps.js +6 -11
  91. package/dist/core/tools/task-steps.js.map +1 -1
  92. package/dist/core/tools/tmux-dispatch.d.ts +2 -0
  93. package/dist/core/tools/tmux-dispatch.d.ts.map +1 -1
  94. package/dist/core/tools/tmux-dispatch.js +16 -2
  95. package/dist/core/tools/tmux-dispatch.js.map +1 -1
  96. package/dist/core/tools/write.d.ts +24 -7
  97. package/dist/core/tools/write.d.ts.map +1 -1
  98. package/dist/core/tools/write.js +182 -107
  99. package/dist/core/tools/write.js.map +1 -1
  100. package/dist/index.d.ts +1 -1
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +1 -1
  103. package/dist/index.js.map +1 -1
  104. package/dist/jscpd-cli.d.ts +3 -0
  105. package/dist/jscpd-cli.d.ts.map +1 -0
  106. package/dist/jscpd-cli.js +22 -0
  107. package/dist/jscpd-cli.js.map +1 -0
  108. package/dist/modes/interactive/components/profile-resource-editor.js +2 -2
  109. package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -1
  110. package/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  111. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  112. package/dist/modes/interactive/components/tool-execution.js +85 -3
  113. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  114. package/dist/modes/interactive/interactive-mode.js +1 -1
  115. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  116. package/dist/utils/bundled-jscpd.d.ts +20 -0
  117. package/dist/utils/bundled-jscpd.d.ts.map +1 -0
  118. package/dist/utils/bundled-jscpd.js +154 -0
  119. package/dist/utils/bundled-jscpd.js.map +1 -0
  120. package/dist/utils/shell.d.ts.map +1 -1
  121. package/dist/utils/shell.js +11 -0
  122. package/dist/utils/shell.js.map +1 -1
  123. package/dist/utils/tools-manager.d.ts +1 -1
  124. package/dist/utils/tools-manager.d.ts.map +1 -1
  125. package/dist/utils/tools-manager.js +22 -0
  126. package/dist/utils/tools-manager.js.map +1 -1
  127. package/docs/index.md +3 -0
  128. package/docs/managed-data-tools.md +15 -0
  129. package/docs/quickstart.md +2 -2
  130. package/docs/tool-boundary-performance-roadmap-2026-08-02.md +166 -0
  131. package/docs/tool-repair.md +1 -1
  132. package/docs/worker-profiles.md +16 -0
  133. package/examples/extensions/built-in-tool-renderer.ts +12 -5
  134. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  135. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  136. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  137. package/examples/extensions/minimal-mode.ts +8 -7
  138. package/examples/extensions/prompt-customizer.ts +4 -2
  139. package/examples/extensions/sandbox/package-lock.json +2 -2
  140. package/examples/extensions/sandbox/package.json +1 -1
  141. package/examples/extensions/ssh.ts +113 -24
  142. package/examples/extensions/with-deps/package-lock.json +2 -2
  143. package/examples/extensions/with-deps/package.json +1 -1
  144. package/npm-shrinkwrap.json +112 -12
  145. package/package.json +7 -5
@@ -7,10 +7,9 @@ export class WorkerProfileResolver {
7
7
  this.options = options;
8
8
  }
9
9
  catalog() {
10
- const activeProfile = this.options.getActiveOrchestrationProfile();
11
- const allowedProfileIds = activeProfile?.role === "orchestrator" ? new Set(activeProfile.dispatchProfileIds) : undefined;
12
- return this.loadProfiles()
13
- .profiles.filter((profile) => profile.role !== "orchestrator" && (!allowedProfileIds || allowedProfileIds.has(profile.profileId)))
10
+ const loaded = this.loadProfiles();
11
+ return loaded.profiles
12
+ .filter((profile) => profile.role !== "orchestrator" && this.isProfileAuthorized(profile.profileId, loaded))
14
13
  .map((profile) => ({
15
14
  profileId: profile.profileId,
16
15
  role: profile.role,
@@ -18,10 +17,21 @@ export class WorkerProfileResolver {
18
17
  }));
19
18
  }
20
19
  resolve(request, defaultProfileId) {
21
- const profileId = request.profileId?.trim() || defaultProfileId;
20
+ const requestedProfileId = request.profileId?.trim();
21
+ const activeProfile = this.options.getActiveOrchestrationProfile();
22
+ const loaded = this.loadProfiles();
23
+ const requestedTaskProfile = requestedProfileId ? loaded.taskRegistry.has(requestedProfileId) : false;
24
+ // A regular session uses the owner's fixed default even if the model invents or mistypes a
25
+ // profile id. Only an owner-authored orchestrator is a routing authority, and its selections
26
+ // remain constrained by dispatchProfileIds in resolveProfileId().
27
+ const profileId = activeProfile?.role === "orchestrator"
28
+ ? requestedProfileId || defaultProfileId
29
+ : requestedTaskProfile
30
+ ? requestedProfileId
31
+ : defaultProfileId || requestedProfileId;
22
32
  if (!profileId)
23
33
  return { ok: false, reason: "orchestration_profile_required" };
24
- const selected = this.resolveProfileId(profileId);
34
+ const selected = this.resolveProfileId(profileId, loaded);
25
35
  if (!selected.ok)
26
36
  return selected;
27
37
  const profile = selected.resolved.profile;
@@ -62,11 +72,11 @@ export class WorkerProfileResolver {
62
72
  },
63
73
  };
64
74
  }
65
- resolveProfileId(profileId) {
66
- if (!this.isProfileAuthorized(profileId)) {
75
+ resolveProfileId(profileId, loaded = this.loadProfiles()) {
76
+ if (!this.isProfileAuthorized(profileId, loaded)) {
67
77
  return { ok: false, reason: "orchestration_profile_not_authorized_for_orchestrator" };
68
78
  }
69
- const profile = this.loadProfiles().registry.get(profileId);
79
+ const profile = loaded.registry.get(profileId);
70
80
  if (!profile)
71
81
  return { ok: false, reason: "orchestration_profile_not_found" };
72
82
  const resolvedModel = resolveConfiguredOrchestrationModel(profile, this.options.getModelRegistry(), (model) => this.options.isModelExhausted(model));
@@ -96,8 +106,15 @@ export class WorkerProfileResolver {
96
106
  },
97
107
  };
98
108
  }
99
- isProfileAuthorized(profileId) {
109
+ isProfileAuthorized(profileId, loaded) {
100
110
  const activeProfile = this.options.getActiveOrchestrationProfile();
111
+ const taskRecord = loaded.taskRegistry.get(profileId);
112
+ if (taskRecord) {
113
+ return (!activeProfile ||
114
+ (activeProfile.role === "orchestrator" &&
115
+ taskRecord.authorProfileId === activeProfile.profileId &&
116
+ activeProfile.dispatchProfileIds.includes(taskRecord.baseProfileId)));
117
+ }
101
118
  return (!activeProfile ||
102
119
  (activeProfile.role === "orchestrator" && activeProfile.dispatchProfileIds.includes(profileId)));
103
120
  }
@@ -114,7 +131,25 @@ export class WorkerProfileResolver {
114
131
  this.warnedDiagnostics.add(key);
115
132
  this.options.onDiagnostic(`Orchestration profile ignored (${diagnostic.path}): ${diagnostic.message}`);
116
133
  }
117
- return loaded;
134
+ const taskProfiles = this.options.getTaskProfileStore().load();
135
+ for (const diagnostic of taskProfiles.diagnostics) {
136
+ const key = `session\0${diagnostic}`;
137
+ if (this.warnedDiagnostics.has(key))
138
+ continue;
139
+ this.warnedDiagnostics.add(key);
140
+ this.options.onDiagnostic(`Session task profile ignored: ${diagnostic}`);
141
+ }
142
+ const profiles = [...loaded.profiles];
143
+ const registry = new Map(loaded.profiles.map((profile) => [profile.profileId, profile]));
144
+ const taskRegistry = new Map();
145
+ for (const record of taskProfiles.records) {
146
+ if (registry.has(record.profile.profileId))
147
+ continue;
148
+ profiles.push(record.profile);
149
+ registry.set(record.profile.profileId, record.profile);
150
+ taskRegistry.set(record.profile.profileId, record);
151
+ }
152
+ return { profiles, registry, taskRegistry };
118
153
  }
119
154
  }
120
155
  //# sourceMappingURL=worker-profile-resolver.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"worker-profile-resolver.js","sourceRoot":"","sources":["../../../src/core/delegation/worker-profile-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Api, Model } from \"@caupulican/pi-ai\";\nimport type { ModelRegistry } from \"../model-registry.ts\";\nimport type {\n\tOrchestrationModelBinding,\n\tOrchestrationProfile,\n\tResourcePointer,\n\tWorkerProfileExecutionContract,\n} from \"../orchestration/contracts.ts\";\nimport {\n\tresolveConfiguredOrchestrationModel,\n\tresolvePinnedOrchestrationModel,\n} from \"../orchestration/model-binding.ts\";\nimport { OrchestrationProfileStore } from \"../orchestration/profile-store.ts\";\nimport type { SettingsManager } from \"../settings-manager.ts\";\nimport type { WorkerDelegationRequest } from \"./worker-delegation-request.ts\";\nimport { catalogWorkerResourcePointers, type WorkerResourceCatalogResourceLoader } from \"./worker-resource-catalog.ts\";\n\nexport interface ResolvedWorkerProfile {\n\tmodel: Model<Api>;\n\tmodelBinding: OrchestrationModelBinding;\n\tprofile: OrchestrationProfile;\n\t/** Metadata-only pointers admitted from the profile-linked resource configuration. */\n\tresourcePointers: readonly ResourcePointer[];\n\tsoul?: string;\n}\n\nexport interface WorkerProfileResolverOptions {\n\tagentDir: string;\n\tcwd: string;\n\tgetSettingsManager(): SettingsManager;\n\tgetResourceLoader(): WorkerResourceCatalogResourceLoader;\n\tgetModelRegistry(): ModelRegistry;\n\tisModelExhausted(model: Model<Api>): boolean;\n\tgetActiveOrchestrationProfile(): OrchestrationProfile | undefined;\n\tonDiagnostic(message: string): void;\n}\n\nexport class WorkerProfileResolver {\n\tprivate readonly options: WorkerProfileResolverOptions;\n\tprivate readonly warnedDiagnostics = new Set<string>();\n\n\tconstructor(options: WorkerProfileResolverOptions) {\n\t\tthis.options = options;\n\t}\n\n\tcatalog(): Array<{ profileId: string; role: string; description: string }> {\n\t\tconst activeProfile = this.options.getActiveOrchestrationProfile();\n\t\tconst allowedProfileIds =\n\t\t\tactiveProfile?.role === \"orchestrator\" ? new Set(activeProfile.dispatchProfileIds) : undefined;\n\t\treturn this.loadProfiles()\n\t\t\t.profiles.filter(\n\t\t\t\t(profile) =>\n\t\t\t\t\tprofile.role !== \"orchestrator\" && (!allowedProfileIds || allowedProfileIds.has(profile.profileId)),\n\t\t\t)\n\t\t\t.map((profile) => ({\n\t\t\t\tprofileId: profile.profileId,\n\t\t\t\trole: profile.role,\n\t\t\t\tdescription: profile.description,\n\t\t\t}));\n\t}\n\n\tresolve(\n\t\trequest: WorkerDelegationRequest,\n\t\tdefaultProfileId: string | undefined,\n\t): { ok: true; resolved: ResolvedWorkerProfile } | { ok: false; reason: string } {\n\t\tconst profileId = request.profileId?.trim() || defaultProfileId;\n\t\tif (!profileId) return { ok: false, reason: \"orchestration_profile_required\" };\n\t\tconst selected = this.resolveProfileId(profileId);\n\t\tif (!selected.ok) return selected;\n\t\tconst profile = selected.resolved.profile;\n\t\tif (request.verificationOfTaskId && profile.role !== \"verifier\") {\n\t\t\treturn { ok: false, reason: \"verification_profile_role_mismatch\" };\n\t\t}\n\t\tif (!request.verificationOfTaskId && profile.role === \"verifier\") {\n\t\t\treturn { ok: false, reason: \"verifier_profile_requires_runtime_dispatch\" };\n\t\t}\n\t\treturn selected;\n\t}\n\n\tresolveVerifier(\n\t\tworkerProfile: OrchestrationProfile,\n\t): { ok: true; resolved: ResolvedWorkerProfile } | { ok: false; reason: string } {\n\t\tif (!workerProfile.requireIndependentVerification || !workerProfile.verificationProfileId) {\n\t\t\treturn { ok: false, reason: \"independent_verifier_not_configured\" };\n\t\t}\n\t\tconst selected = this.resolveProfileId(workerProfile.verificationProfileId);\n\t\tif (!selected.ok) return selected;\n\t\tif (selected.resolved.profile.role !== \"verifier\") {\n\t\t\treturn { ok: false, reason: \"verification_profile_role_mismatch\" };\n\t\t}\n\t\treturn selected;\n\t}\n\n\tresolveContract(\n\t\tcontract: WorkerProfileExecutionContract,\n\t): { ok: true; resolved: ResolvedWorkerProfile } | { ok: false; reason: string } {\n\t\t// The architect allowlist authorized this immutable contract at admission. Live session/goal\n\t\t// controls may revoke execution, but mutable profile files cannot retroactively redefine it.\n\t\tconst resolvedModel = resolvePinnedOrchestrationModel(\n\t\t\tcontract.modelBinding,\n\t\t\tthis.options.getModelRegistry(),\n\t\t\t(model) => this.options.isModelExhausted(model),\n\t\t);\n\t\tif (!resolvedModel) return { ok: false, reason: \"orchestration_profile_model_unavailable\" };\n\t\treturn {\n\t\t\tok: true,\n\t\t\tresolved: {\n\t\t\t\tmodel: resolvedModel.model,\n\t\t\t\tmodelBinding: resolvedModel.binding,\n\t\t\t\tprofile: structuredClone(contract.profile),\n\t\t\t\tresourcePointers: structuredClone(contract.resourcePointers),\n\t\t\t\t...(contract.soul ? { soul: contract.soul } : {}),\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate resolveProfileId(\n\t\tprofileId: string,\n\t): { ok: true; resolved: ResolvedWorkerProfile } | { ok: false; reason: string } {\n\t\tif (!this.isProfileAuthorized(profileId)) {\n\t\t\treturn { ok: false, reason: \"orchestration_profile_not_authorized_for_orchestrator\" };\n\t\t}\n\t\tconst profile = this.loadProfiles().registry.get(profileId);\n\t\tif (!profile) return { ok: false, reason: \"orchestration_profile_not_found\" };\n\t\tconst resolvedModel = resolveConfiguredOrchestrationModel(profile, this.options.getModelRegistry(), (model) =>\n\t\t\tthis.options.isModelExhausted(model),\n\t\t);\n\t\tif (!resolvedModel) return { ok: false, reason: \"orchestration_profile_model_unavailable\" };\n\t\tconst linkedProfiles = profile.resourceProfileNames.map((name) =>\n\t\t\tthis.options.getSettingsManager().getProfileRegistry().getProfile(name),\n\t\t);\n\t\tif (linkedProfiles.some((linked) => linked === undefined)) {\n\t\t\treturn { ok: false, reason: \"orchestration_resource_profile_not_found\" };\n\t\t}\n\t\tconst soul = linkedProfiles\n\t\t\t.flatMap((linked) => (linked?.soul ? [linked.soul] : []))\n\t\t\t.join(\"\\n\\n\")\n\t\t\t.trim();\n\t\tconst resourcePointers = catalogWorkerResourcePointers({\n\t\t\tcwd: this.options.cwd,\n\t\t\tresourceLoader: this.options.getResourceLoader(),\n\t\t\tresourceProfiles: linkedProfiles.filter(\n\t\t\t\t(linked): linked is NonNullable<typeof linked> => linked !== undefined,\n\t\t\t),\n\t\t});\n\t\treturn {\n\t\t\tok: true,\n\t\t\tresolved: {\n\t\t\t\tmodel: resolvedModel.model,\n\t\t\t\tmodelBinding: resolvedModel.binding,\n\t\t\t\tprofile,\n\t\t\t\tresourcePointers,\n\t\t\t\t...(soul ? { soul } : {}),\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate isProfileAuthorized(profileId: string): boolean {\n\t\tconst activeProfile = this.options.getActiveOrchestrationProfile();\n\t\treturn (\n\t\t\t!activeProfile ||\n\t\t\t(activeProfile.role === \"orchestrator\" && activeProfile.dispatchProfileIds.includes(profileId))\n\t\t);\n\t}\n\n\tprivate loadProfiles(): ReturnType<OrchestrationProfileStore[\"load\"]> {\n\t\tconst loaded = new OrchestrationProfileStore({\n\t\t\tagentDir: this.options.agentDir,\n\t\t\tcwd: this.options.cwd,\n\t\t\tprojectTrusted: this.options.getSettingsManager().isProjectTrusted(),\n\t\t}).load();\n\t\tfor (const diagnostic of loaded.diagnostics) {\n\t\t\tconst key = `${diagnostic.path}\\0${diagnostic.message}`;\n\t\t\tif (this.warnedDiagnostics.has(key)) continue;\n\t\t\tthis.warnedDiagnostics.add(key);\n\t\t\tthis.options.onDiagnostic(`Orchestration profile ignored (${diagnostic.path}): ${diagnostic.message}`);\n\t\t}\n\t\treturn loaded;\n\t}\n}\n"]}
1
+ {"version":3,"file":"worker-profile-resolver.js","sourceRoot":"","sources":["../../../src/core/delegation/worker-profile-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Api, Model } from \"@caupulican/pi-ai\";\nimport type { ModelRegistry } from \"../model-registry.ts\";\nimport type {\n\tOrchestrationModelBinding,\n\tOrchestrationProfile,\n\tResourcePointer,\n\tWorkerProfileExecutionContract,\n} from \"../orchestration/contracts.ts\";\nimport {\n\tresolveConfiguredOrchestrationModel,\n\tresolvePinnedOrchestrationModel,\n} from \"../orchestration/model-binding.ts\";\nimport { OrchestrationProfileStore } from \"../orchestration/profile-store.ts\";\nimport type { SessionTaskProfileRecord, SessionTaskProfileStore } from \"../orchestration/session-task-profile-store.ts\";\nimport type { SettingsManager } from \"../settings-manager.ts\";\nimport type { WorkerDelegationRequest } from \"./worker-delegation-request.ts\";\nimport { catalogWorkerResourcePointers, type WorkerResourceCatalogResourceLoader } from \"./worker-resource-catalog.ts\";\n\nexport interface ResolvedWorkerProfile {\n\tmodel: Model<Api>;\n\tmodelBinding: OrchestrationModelBinding;\n\tprofile: OrchestrationProfile;\n\t/** Metadata-only pointers admitted from the profile-linked resource configuration. */\n\tresourcePointers: readonly ResourcePointer[];\n\tsoul?: string;\n}\n\nexport interface WorkerProfileResolverOptions {\n\tagentDir: string;\n\tcwd: string;\n\tgetSettingsManager(): SettingsManager;\n\tgetResourceLoader(): WorkerResourceCatalogResourceLoader;\n\tgetModelRegistry(): ModelRegistry;\n\tisModelExhausted(model: Model<Api>): boolean;\n\tgetActiveOrchestrationProfile(): OrchestrationProfile | undefined;\n\tgetTaskProfileStore(): SessionTaskProfileStore;\n\tonDiagnostic(message: string): void;\n}\n\ninterface LoadedWorkerProfiles {\n\tprofiles: OrchestrationProfile[];\n\tregistry: ReadonlyMap<string, OrchestrationProfile>;\n\ttaskRegistry: ReadonlyMap<string, SessionTaskProfileRecord>;\n}\n\nexport class WorkerProfileResolver {\n\tprivate readonly options: WorkerProfileResolverOptions;\n\tprivate readonly warnedDiagnostics = new Set<string>();\n\n\tconstructor(options: WorkerProfileResolverOptions) {\n\t\tthis.options = options;\n\t}\n\n\tcatalog(): Array<{ profileId: string; role: string; description: string }> {\n\t\tconst loaded = this.loadProfiles();\n\t\treturn loaded.profiles\n\t\t\t.filter((profile) => profile.role !== \"orchestrator\" && this.isProfileAuthorized(profile.profileId, loaded))\n\t\t\t.map((profile) => ({\n\t\t\t\tprofileId: profile.profileId,\n\t\t\t\trole: profile.role,\n\t\t\t\tdescription: profile.description,\n\t\t\t}));\n\t}\n\n\tresolve(\n\t\trequest: WorkerDelegationRequest,\n\t\tdefaultProfileId: string | undefined,\n\t): { ok: true; resolved: ResolvedWorkerProfile } | { ok: false; reason: string } {\n\t\tconst requestedProfileId = request.profileId?.trim();\n\t\tconst activeProfile = this.options.getActiveOrchestrationProfile();\n\t\tconst loaded = this.loadProfiles();\n\t\tconst requestedTaskProfile = requestedProfileId ? loaded.taskRegistry.has(requestedProfileId) : false;\n\t\t// A regular session uses the owner's fixed default even if the model invents or mistypes a\n\t\t// profile id. Only an owner-authored orchestrator is a routing authority, and its selections\n\t\t// remain constrained by dispatchProfileIds in resolveProfileId().\n\t\tconst profileId =\n\t\t\tactiveProfile?.role === \"orchestrator\"\n\t\t\t\t? requestedProfileId || defaultProfileId\n\t\t\t\t: requestedTaskProfile\n\t\t\t\t\t? requestedProfileId\n\t\t\t\t\t: defaultProfileId || requestedProfileId;\n\t\tif (!profileId) return { ok: false, reason: \"orchestration_profile_required\" };\n\t\tconst selected = this.resolveProfileId(profileId, loaded);\n\t\tif (!selected.ok) return selected;\n\t\tconst profile = selected.resolved.profile;\n\t\tif (request.verificationOfTaskId && profile.role !== \"verifier\") {\n\t\t\treturn { ok: false, reason: \"verification_profile_role_mismatch\" };\n\t\t}\n\t\tif (!request.verificationOfTaskId && profile.role === \"verifier\") {\n\t\t\treturn { ok: false, reason: \"verifier_profile_requires_runtime_dispatch\" };\n\t\t}\n\t\treturn selected;\n\t}\n\n\tresolveVerifier(\n\t\tworkerProfile: OrchestrationProfile,\n\t): { ok: true; resolved: ResolvedWorkerProfile } | { ok: false; reason: string } {\n\t\tif (!workerProfile.requireIndependentVerification || !workerProfile.verificationProfileId) {\n\t\t\treturn { ok: false, reason: \"independent_verifier_not_configured\" };\n\t\t}\n\t\tconst selected = this.resolveProfileId(workerProfile.verificationProfileId);\n\t\tif (!selected.ok) return selected;\n\t\tif (selected.resolved.profile.role !== \"verifier\") {\n\t\t\treturn { ok: false, reason: \"verification_profile_role_mismatch\" };\n\t\t}\n\t\treturn selected;\n\t}\n\n\tresolveContract(\n\t\tcontract: WorkerProfileExecutionContract,\n\t): { ok: true; resolved: ResolvedWorkerProfile } | { ok: false; reason: string } {\n\t\t// The architect allowlist authorized this immutable contract at admission. Live session/goal\n\t\t// controls may revoke execution, but mutable profile files cannot retroactively redefine it.\n\t\tconst resolvedModel = resolvePinnedOrchestrationModel(\n\t\t\tcontract.modelBinding,\n\t\t\tthis.options.getModelRegistry(),\n\t\t\t(model) => this.options.isModelExhausted(model),\n\t\t);\n\t\tif (!resolvedModel) return { ok: false, reason: \"orchestration_profile_model_unavailable\" };\n\t\treturn {\n\t\t\tok: true,\n\t\t\tresolved: {\n\t\t\t\tmodel: resolvedModel.model,\n\t\t\t\tmodelBinding: resolvedModel.binding,\n\t\t\t\tprofile: structuredClone(contract.profile),\n\t\t\t\tresourcePointers: structuredClone(contract.resourcePointers),\n\t\t\t\t...(contract.soul ? { soul: contract.soul } : {}),\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate resolveProfileId(\n\t\tprofileId: string,\n\t\tloaded: LoadedWorkerProfiles = this.loadProfiles(),\n\t): { ok: true; resolved: ResolvedWorkerProfile } | { ok: false; reason: string } {\n\t\tif (!this.isProfileAuthorized(profileId, loaded)) {\n\t\t\treturn { ok: false, reason: \"orchestration_profile_not_authorized_for_orchestrator\" };\n\t\t}\n\t\tconst profile = loaded.registry.get(profileId);\n\t\tif (!profile) return { ok: false, reason: \"orchestration_profile_not_found\" };\n\t\tconst resolvedModel = resolveConfiguredOrchestrationModel(profile, this.options.getModelRegistry(), (model) =>\n\t\t\tthis.options.isModelExhausted(model),\n\t\t);\n\t\tif (!resolvedModel) return { ok: false, reason: \"orchestration_profile_model_unavailable\" };\n\t\tconst linkedProfiles = profile.resourceProfileNames.map((name) =>\n\t\t\tthis.options.getSettingsManager().getProfileRegistry().getProfile(name),\n\t\t);\n\t\tif (linkedProfiles.some((linked) => linked === undefined)) {\n\t\t\treturn { ok: false, reason: \"orchestration_resource_profile_not_found\" };\n\t\t}\n\t\tconst soul = linkedProfiles\n\t\t\t.flatMap((linked) => (linked?.soul ? [linked.soul] : []))\n\t\t\t.join(\"\\n\\n\")\n\t\t\t.trim();\n\t\tconst resourcePointers = catalogWorkerResourcePointers({\n\t\t\tcwd: this.options.cwd,\n\t\t\tresourceLoader: this.options.getResourceLoader(),\n\t\t\tresourceProfiles: linkedProfiles.filter(\n\t\t\t\t(linked): linked is NonNullable<typeof linked> => linked !== undefined,\n\t\t\t),\n\t\t});\n\t\treturn {\n\t\t\tok: true,\n\t\t\tresolved: {\n\t\t\t\tmodel: resolvedModel.model,\n\t\t\t\tmodelBinding: resolvedModel.binding,\n\t\t\t\tprofile,\n\t\t\t\tresourcePointers,\n\t\t\t\t...(soul ? { soul } : {}),\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate isProfileAuthorized(profileId: string, loaded: LoadedWorkerProfiles): boolean {\n\t\tconst activeProfile = this.options.getActiveOrchestrationProfile();\n\t\tconst taskRecord = loaded.taskRegistry.get(profileId);\n\t\tif (taskRecord) {\n\t\t\treturn (\n\t\t\t\t!activeProfile ||\n\t\t\t\t(activeProfile.role === \"orchestrator\" &&\n\t\t\t\t\ttaskRecord.authorProfileId === activeProfile.profileId &&\n\t\t\t\t\tactiveProfile.dispatchProfileIds.includes(taskRecord.baseProfileId))\n\t\t\t);\n\t\t}\n\t\treturn (\n\t\t\t!activeProfile ||\n\t\t\t(activeProfile.role === \"orchestrator\" && activeProfile.dispatchProfileIds.includes(profileId))\n\t\t);\n\t}\n\n\tprivate loadProfiles(): LoadedWorkerProfiles {\n\t\tconst loaded = new OrchestrationProfileStore({\n\t\t\tagentDir: this.options.agentDir,\n\t\t\tcwd: this.options.cwd,\n\t\t\tprojectTrusted: this.options.getSettingsManager().isProjectTrusted(),\n\t\t}).load();\n\t\tfor (const diagnostic of loaded.diagnostics) {\n\t\t\tconst key = `${diagnostic.path}\\0${diagnostic.message}`;\n\t\t\tif (this.warnedDiagnostics.has(key)) continue;\n\t\t\tthis.warnedDiagnostics.add(key);\n\t\t\tthis.options.onDiagnostic(`Orchestration profile ignored (${diagnostic.path}): ${diagnostic.message}`);\n\t\t}\n\t\tconst taskProfiles = this.options.getTaskProfileStore().load();\n\t\tfor (const diagnostic of taskProfiles.diagnostics) {\n\t\t\tconst key = `session\\0${diagnostic}`;\n\t\t\tif (this.warnedDiagnostics.has(key)) continue;\n\t\t\tthis.warnedDiagnostics.add(key);\n\t\t\tthis.options.onDiagnostic(`Session task profile ignored: ${diagnostic}`);\n\t\t}\n\t\tconst profiles = [...loaded.profiles];\n\t\tconst registry = new Map(loaded.profiles.map((profile) => [profile.profileId, profile]));\n\t\tconst taskRegistry = new Map<string, SessionTaskProfileRecord>();\n\t\tfor (const record of taskProfiles.records) {\n\t\t\tif (registry.has(record.profile.profileId)) continue;\n\t\t\tprofiles.push(record.profile);\n\t\t\tregistry.set(record.profile.profileId, record.profile);\n\t\t\ttaskRegistry.set(record.profile.profileId, record);\n\t\t}\n\t\treturn { profiles, registry, taskRegistry };\n\t}\n}\n"]}
@@ -45,7 +45,7 @@ export interface DoctorDeps {
45
45
  loadAvailableFffNodePackage: () => unknown | undefined;
46
46
  ensureFffNodePackage: (silent?: boolean) => Promise<unknown | undefined>;
47
47
  getLastFffInstallOutcome: () => FffInstallOutcome | undefined;
48
- ensureTool: (tool: "jq" | "rg", silent: boolean) => Promise<string | undefined>;
48
+ ensureTool: (tool: "jq" | "jscpd" | "rg", silent: boolean) => Promise<string | undefined>;
49
49
  ensurePythonRuntime: (options: {
50
50
  silent: boolean;
51
51
  }) => Promise<PythonRuntimeOutcome>;
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/core/doctor.ts"],"names":[],"mappings":"AAEA,OAAO,EAGN,KAAK,iBAAiB,EAItB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,KAAK,0BAA0B,EAA+B,MAAM,6BAA6B,CAAC;AAC3G,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAuB,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAErF;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,QAAQ,CAAC;AAElD,MAAM,WAAW,WAAW;IAC3B,+EAA+E;IAC/E,EAAE,EAAE,MAAM,CAAC;IACX,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,uFAAuF;IACvF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf;AAED,qHAAqH;AACrH,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,cAAc,CAAC,CAAC;AAEjF,MAAM,WAAW,UAAU;IAC1B,2BAA2B,EAAE,MAAM,OAAO,GAAG,SAAS,CAAC;IACvD,oBAAoB,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IACzE,wBAAwB,EAAE,MAAM,iBAAiB,GAAG,SAAS,CAAC;IAC9D,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,EAAE,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAChF,mBAAmB,EAAE,CAAC,OAAO,EAAE;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACrF,8IAA8I;IAC9I,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,GAAG,SAAS,CAAC;IACvF,aAAa,EAAE,mBAAmB,CAAC;IACnC,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;CAC9D;AAED,MAAM,WAAW,gBAAgB;IAChC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AAiJD,wBAAsB,SAAS,CAC9B,IAAI,GAAE,UAA2B,EACjC,OAAO,GAAE,gBAAqB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAUvB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAc/D;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,GAAE,UAA2B,GAAG,OAAO,CAAC,IAAI,CAAC,CAOzF"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/core/doctor.ts"],"names":[],"mappings":"AAEA,OAAO,EAGN,KAAK,iBAAiB,EAItB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,KAAK,0BAA0B,EAA+B,MAAM,6BAA6B,CAAC;AAC3G,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAuB,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAErF;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,QAAQ,CAAC;AAElD,MAAM,WAAW,WAAW;IAC3B,+EAA+E;IAC/E,EAAE,EAAE,MAAM,CAAC;IACX,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,uFAAuF;IACvF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf;AAED,qHAAqH;AACrH,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,cAAc,CAAC,CAAC;AAEjF,MAAM,WAAW,UAAU;IAC1B,2BAA2B,EAAE,MAAM,OAAO,GAAG,SAAS,CAAC;IACvD,oBAAoB,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IACzE,wBAAwB,EAAE,MAAM,iBAAiB,GAAG,SAAS,CAAC;IAC9D,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,GAAG,IAAI,EAAE,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC1F,mBAAmB,EAAE,CAAC,OAAO,EAAE;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACrF,8IAA8I;IAC9I,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,GAAG,SAAS,CAAC;IACvF,aAAa,EAAE,mBAAmB,CAAC;IACnC,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;CAC9D;AAED,MAAM,WAAW,gBAAgB;IAChC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AAsJD,wBAAsB,SAAS,CAC9B,IAAI,GAAE,UAA2B,EACjC,OAAO,GAAE,gBAAqB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAWvB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAc/D;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,GAAE,UAA2B,GAAG,OAAO,CAAC,IAAI,CAAC,CAOzF"}
@@ -55,8 +55,8 @@ async function checkFffNode(deps, silent) {
55
55
  /** MANAGED tools: exact releases and checksums are owned by tools-manager. */
56
56
  async function checkManagedDataTool(deps, tool, silent) {
57
57
  const path = await deps.ensureTool(tool, silent);
58
- const id = tool === "rg" ? "ripgrep" : "jq";
59
- const label = tool === "rg" ? "ripgrep (rg)" : "jq (JSON projection)";
58
+ const id = tool === "rg" ? "ripgrep" : tool;
59
+ const label = tool === "rg" ? "ripgrep (rg)" : tool === "jscpd" ? "jscpd v5 (clone scanner)" : "jq (JSON projection)";
60
60
  if (!path)
61
61
  return { id, label, kind: "managed", present: false, detail: "managed install unavailable" };
62
62
  const version = firstLine(deps.probeVersion(path));
@@ -142,14 +142,15 @@ function checkAgentDirectoryLayout(deps) {
142
142
  }
143
143
  export async function runDoctor(deps = realDoctorDeps, options = {}) {
144
144
  const silent = options.silent ?? true;
145
- const [fffNode, ripgrep, jq, ollama, python] = await Promise.all([
145
+ const [fffNode, ripgrep, jq, jscpd, ollama, python] = await Promise.all([
146
146
  checkFffNode(deps, silent),
147
147
  checkManagedDataTool(deps, "rg", silent),
148
148
  checkManagedDataTool(deps, "jq", silent),
149
+ checkManagedDataTool(deps, "jscpd", silent),
149
150
  checkOllama(deps),
150
151
  checkPython(deps, silent),
151
152
  ]);
152
- return { checks: [fffNode, ripgrep, jq, ollama, python], notices: checkAgentDirectoryLayout(deps) };
153
+ return { checks: [fffNode, ripgrep, jq, jscpd, ollama, python], notices: checkAgentDirectoryLayout(deps) };
153
154
  }
154
155
  export function formatDoctorReport(report) {
155
156
  const lines = [];
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/core/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EACN,oBAAoB,EACpB,UAAU,EAEV,wBAAwB,EACxB,2BAA2B,EAC3B,YAAY,GACZ,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAmC,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAC3G,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAA6B,MAAM,qBAAqB,CAAC;AAqErF,MAAM,cAAc,GAAe;IAClC,2BAA2B;IAC3B,oBAAoB;IACpB,wBAAwB;IACxB,UAAU;IACV,mBAAmB;IACnB,YAAY;IACZ,aAAa,EAAE,IAAI,aAAa,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,CAAC;IAC7D,2BAA2B,EAAE,GAAG,EAAE,CAAC,2BAA2B,CAAC,WAAW,EAAE,CAAC;CAC7E,CAAC;AAEF,SAAS,kBAAkB,CAAC,OAAsC;IACjE,QAAQ,OAAO,EAAE,MAAM,EAAE,CAAC;QACzB,KAAK,mBAAmB;YACvB,OAAO,mBAAmB,CAAC;QAC5B,KAAK,WAAW;YACf,OAAO,oBAAoB,CAAC;QAC7B,KAAK,SAAS;YACb,OAAO,+BAA+B,CAAC;QACxC,KAAK,sBAAsB;YAC1B,OAAO,mCAAmC,CAAC;QAC5C,KAAK,gBAAgB;YACpB,OAAO,mBAAmB,OAAO,CAAC,MAAM,EAAE,CAAC;QAC5C;YACC,OAAO,aAAa,CAAC;IACvB,CAAC;AACF,CAAC;AAED,wHAAwH;AACxH,SAAS,SAAS,CAAC,IAAwB;IAC1C,OAAO,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7B,CAAC;AAED,gGAAgG;AAChG,KAAK,UAAU,YAAY,CAAC,IAAgB,EAAE,MAAe;IAC5D,2EAA2E;IAC3E,4EAA4E;IAC5E,6EAA6E;IAC7E,qEAAqE;IACrE,0EAA0E;IAC1E,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,IAAI,CAAC,wBAAwB,EAAE,CAAC;IAChD,OAAO;QACN,EAAE,EAAE,UAAU;QACd,KAAK,EAAE,uCAAuC;QAC9C,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC;QAC3B,gBAAgB,EAAE,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,KAAK,mBAAmB;QACjF,MAAM,EAAE,kBAAkB,CAAC,OAAO,CAAC;KACnC,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,KAAK,UAAU,oBAAoB,CAAC,IAAgB,EAAE,IAAiB,EAAE,MAAe;IACvF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACjD,MAAM,EAAE,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5C,MAAM,KAAK,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,sBAAsB,CAAC;IACtE,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,6BAA6B,EAAE,CAAC;IACxG,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;IACnD,OAAO;QACN,EAAE;QACF,KAAK;QACL,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI;KAC/C,CAAC;AACH,CAAC;AAED,oIAAoI;AACpI,KAAK,UAAU,WAAW,CAAC,IAAgB;IAC1C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC;IACjD,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QACxB,OAAO;YACN,EAAE,EAAE,QAAQ;YACZ,KAAK,EAAE,8BAA8B;YACrC,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,IAAI,CAAC,aAAa,CAAC,YAAY,EAAE;SACxC,CAAC;IACH,CAAC;IACD,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IAChE,MAAM,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACpD,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW;QACrC,CAAC,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,KAAK,MAAM,CAAC,WAAW,CAAC,IAAI,KAAK,MAAM,CAAC,WAAW,CAAC,UAAU,YAAY;QACtG,CAAC,CAAC,wBAAwB,MAAM,CAAC,cAAc,QAAQ,MAAM,CAAC,UAAU,CAAC,UAAU,WAAW,CAAC;IAChG,OAAO;QACN,EAAE,EAAE,QAAQ;QACZ,KAAK,EAAE,8BAA8B;QACrC,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,WAAW,MAAM,CAAC,UAAU,KAAK,MAAM,CAAC,YAAY,IAAI,aAAa,aAAa,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,OAAO,MAAM,CAAC,SAAS,mBAAmB,WAAW,EAAE;KAChL,CAAC;AACH,CAAC;AAED,oGAAoG;AACpG,KAAK,UAAU,WAAW,CAAC,IAAgB,EAAE,MAAe;IAC3D,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3D,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;QAChC,OAAO;YACN,EAAE,EAAE,QAAQ;YACZ,KAAK,EAAE,qBAAqB;YAC5B,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,OAAO,CAAC,MAAM;SACtB,CAAC;IACH,CAAC;IACD,OAAO;QACN,EAAE,EAAE,QAAQ;QACZ,KAAK,EAAE,qBAAqB;QAC5B,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,IAAI;QACb,gBAAgB,EAAE,OAAO,CAAC,eAAe;QACzC,MAAM,EAAE,OAAO,OAAO,CAAC,MAAM,aAAa,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,EAAE,EAAE;KACvH,CAAC;AACH,CAAC;AAED,SAAS,yBAAyB,CAAC,IAAgB;IAClD,MAAM,MAAM,GAAG,IAAI,CAAC,2BAA2B,EAAE,CAAC;IAClD,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAClB,OAAO;YACN;gBACC,EAAE,EAAE,wBAAwB;gBAC5B,KAAK,EAAE,wBAAwB;gBAC/B,MAAM,EAAE,qBAAqB,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,KAAK,EAAE;aAC/D;SACD,CAAC;IACH,CAAC;IACD,IAAI,MAAM,CAAC,oBAAoB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS;QAAE,OAAO,EAAE,CAAC;IAEtE,MAAM,MAAM,GAAG,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,MAAM,CAAC,oBAAoB,GAAG,MAAM,CAAC,iBAAiB,CAAC,MAAM,CAAC;IAC9E,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,MAAM,CAAC,oBAAoB,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IACjH,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACvF,MAAM,UAAU,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,wBAAwB,MAAM,CAAC,cAAc,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;IACnG,OAAO;QACN;YACC,EAAE,EAAE,wBAAwB;YAC5B,KAAK,EAAE,wBAAwB;YAC/B,MAAM,EAAE,GAAG,KAAK,wBAAwB,MAAM,CAAC,oBAAoB,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,OAAO,GAAG,UAAU,8FAA8F;SAC5M;KACD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC9B,IAAI,GAAe,cAAc,EACjC,OAAO,GAAqB,EAAE;IAE9B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC;IACtC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAChE,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC;QAC1B,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC;QACxC,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC;QACxC,WAAW,CAAC,IAAI,CAAC;QACjB,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC;KACzB,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,yBAAyB,CAAC,IAAI,CAAC,EAAE,CAAC;AACrG,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAoB;IACtD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAC/E,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzD,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,KAAK,CAAC,KAAK,GAAG,MAAM,EAAE,CAAC,CAAC;QAChD,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YACnC,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,KAAK;gBAAE,KAAK,CAAC,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC,CAAC;QACnE,CAAC;IACF,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QAC3C,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,KAAK,OAAO,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAI,GAAe,cAAc;IACzE,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,KAAK,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAClD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,+BAA+B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAClH,CAAC;AACF,CAAC","sourcesContent":["import chalk from \"chalk\";\nimport { getAgentDir } from \"../config.ts\";\nimport {\n\tensureFffNodePackage,\n\tensureTool,\n\ttype FffInstallOutcome,\n\tgetLastFffInstallOutcome,\n\tloadAvailableFffNodePackage,\n\tprobeVersion,\n} from \"../utils/tools-manager.ts\";\nimport { type AgentDirectoryLayoutReport, inspectAgentDirectoryLayout } from \"./agent-directory-layout.ts\";\nimport { OllamaRuntime } from \"./models/local-runtime.ts\";\nimport { ensurePythonRuntime, type PythonRuntimeOutcome } from \"./python-runtime.ts\";\n\n/**\n * Environment doctor: verifies required tooling and installs what it safely\n * can, instead of leaving provisioning entirely to lazy first-use (the exact\n * gap behind \"ran `pi-adaptative update` on another machine and fff-node\n * never got installed\" -- see fff-lazy-install.test.ts/fff-search-tools.test.ts\n * for the underlying lazy-install fix; this module is the proactive half).\n *\n * Two tool kinds, two different postures:\n * - \"managed\": pi owns provisioning and attempts it when absent (fff-node,\n * pinned rg/jq/uv, and the Python interpreter resolved through uv).\n * - \"system\": pi does not own the install (Ollama). GUIDE MODE only -- exact\n * manual steps are reported, never executed.\n */\n\nexport type DoctorToolKind = \"managed\" | \"system\";\n\nexport interface DoctorCheck {\n\t/** Stable identifier, e.g. \"fff-node\", \"ripgrep\", \"jq\", \"ollama\", \"python\". */\n\tid: string;\n\t/** Human-readable label for the report. */\n\tlabel: string;\n\tkind: DoctorToolKind;\n\tpresent: boolean;\n\t/** Version/path/status summary, present or not. */\n\tdetail?: string;\n\t/** Only set for a \"managed\" tool: whether an install was just attempted. */\n\tinstallAttempted?: boolean;\n\t/** Only set for a \"system\" tool that's missing: exact manual steps, never executed. */\n\tguide?: string[];\n}\n\nexport interface DoctorReport {\n\tchecks: DoctorCheck[];\n\tnotices?: DoctorNotice[];\n}\n\nexport interface DoctorNotice {\n\tid: string;\n\tlabel: string;\n\tdetail: string;\n}\n\n/** Minimal slice of OllamaRuntime the doctor needs -- lets tests inject a fake without touching the real runtime. */\nexport type DoctorOllamaRuntime = Pick<OllamaRuntime, \"detect\" | \"installGuide\">;\n\nexport interface DoctorDeps {\n\tloadAvailableFffNodePackage: () => unknown | undefined;\n\tensureFffNodePackage: (silent?: boolean) => Promise<unknown | undefined>;\n\tgetLastFffInstallOutcome: () => FffInstallOutcome | undefined;\n\tensureTool: (tool: \"jq\" | \"rg\", silent: boolean) => Promise<string | undefined>;\n\tensurePythonRuntime: (options: { silent: boolean }) => Promise<PythonRuntimeOutcome>;\n\t/** Best-effort `<command> --version` probe; undefined if it can't be run. Used only to enrich a status line, never for presence detection. */\n\tprobeVersion: (command: string, versionArgs?: readonly string[]) => string | undefined;\n\tollamaRuntime: DoctorOllamaRuntime;\n\tinspectAgentDirectoryLayout: () => AgentDirectoryLayoutReport;\n}\n\nexport interface RunDoctorOptions {\n\t/**\n\t * Whether the fff-node managed install (if one is attempted) stays quiet.\n\t * Default true, matching runUpdatePreflight's existing background\n\t * behavior. The interactive `doctor` command passes `false` so a\n\t * multi-second install doesn't read as a silent hang.\n\t */\n\tsilent?: boolean;\n}\n\nconst realDoctorDeps: DoctorDeps = {\n\tloadAvailableFffNodePackage,\n\tensureFffNodePackage,\n\tgetLastFffInstallOutcome,\n\tensureTool,\n\tensurePythonRuntime,\n\tprobeVersion,\n\tollamaRuntime: new OllamaRuntime({ agentDir: getAgentDir() }),\n\tinspectAgentDirectoryLayout: () => inspectAgentDirectoryLayout(getAgentDir()),\n};\n\nfunction describeFffOutcome(outcome: FffInstallOutcome | undefined): string {\n\tswitch (outcome?.status) {\n\t\tcase \"already-available\":\n\t\t\treturn \"already available\";\n\t\tcase \"installed\":\n\t\t\treturn \"installed just now\";\n\t\tcase \"offline\":\n\t\t\treturn \"offline mode enabled, skipped\";\n\t\tcase \"unsupported-platform\":\n\t\t\treturn \"no native build for this platform\";\n\t\tcase \"install-failed\":\n\t\t\treturn `install failed: ${outcome.reason}`;\n\t\tdefault:\n\t\t\treturn \"unavailable\";\n\t}\n}\n\n/** First line only -- a compiled-features/build-flags tail (e.g. rg --version) is noise in a one-line status report. */\nfunction firstLine(text: string | undefined): string | undefined {\n\treturn text?.split(\"\\n\")[0];\n}\n\n/** MANAGED tool: pi owns this install path, so the doctor actually attempts it when missing. */\nasync function checkFffNode(deps: DoctorDeps, silent: boolean): Promise<DoctorCheck> {\n\t// Delegate entirely to ensureFffNodePackage rather than re-deriving \"is it\n\t// already there\" here: that function (and its offline/unsupported-platform/\n\t// cooldown handling, see tools-manager.ts) is the single source of truth for\n\t// whether an install should even be attempted -- the doctor must not\n\t// duplicate or second-guess that logic, only report its outcome honestly.\n\tconst installed = await deps.ensureFffNodePackage(silent);\n\tconst outcome = deps.getLastFffInstallOutcome();\n\treturn {\n\t\tid: \"fff-node\",\n\t\tlabel: \"FFF native search (@ff-labs/fff-node)\",\n\t\tkind: \"managed\",\n\t\tpresent: Boolean(installed),\n\t\tinstallAttempted: outcome !== undefined && outcome.status !== \"already-available\",\n\t\tdetail: describeFffOutcome(outcome),\n\t};\n}\n\n/** MANAGED tools: exact releases and checksums are owned by tools-manager. */\nasync function checkManagedDataTool(deps: DoctorDeps, tool: \"jq\" | \"rg\", silent: boolean): Promise<DoctorCheck> {\n\tconst path = await deps.ensureTool(tool, silent);\n\tconst id = tool === \"rg\" ? \"ripgrep\" : \"jq\";\n\tconst label = tool === \"rg\" ? \"ripgrep (rg)\" : \"jq (JSON projection)\";\n\tif (!path) return { id, label, kind: \"managed\", present: false, detail: \"managed install unavailable\" };\n\tconst version = firstLine(deps.probeVersion(path));\n\treturn {\n\t\tid,\n\t\tlabel,\n\t\tkind: \"managed\",\n\t\tpresent: true,\n\t\tdetail: version ? `${path} (${version})` : path,\n\t};\n}\n\n/** SYSTEM tool: guide mode only, never installed by the doctor (OllamaRuntime.installGuide() is itself already guide-mode-only). */\nasync function checkOllama(deps: DoctorDeps): Promise<DoctorCheck> {\n\tconst status = await deps.ollamaRuntime.detect();\n\tif (!status.binaryPath) {\n\t\treturn {\n\t\t\tid: \"ollama\",\n\t\t\tlabel: \"Ollama (local model runtime)\",\n\t\t\tkind: \"system\",\n\t\t\tpresent: false,\n\t\t\tguide: deps.ollamaRuntime.installGuide(),\n\t\t};\n\t}\n\tconst version = firstLine(deps.probeVersion(status.binaryPath));\n\tconst versionSuffix = version ? `, ${version}` : \"\";\n\tconst activeStore = status.activeStore\n\t\t? `${status.activeStore.path} [${status.activeStore.kind}, ${status.activeStore.modelCount} model(s)]`\n\t\t: `none; pi-owned store ${status.ownedModelsDir} has ${status.ownedStore.modelCount} model(s)`;\n\treturn {\n\t\tid: \"ollama\",\n\t\tlabel: \"Ollama (local model runtime)\",\n\t\tkind: \"system\",\n\t\tpresent: true,\n\t\tdetail: `binary: ${status.binaryPath} [${status.binarySource}]${versionSuffix}; server: ${status.serverUp ? \"up\" : \"down\"} at ${status.serverUrl}; active store: ${activeStore}`,\n\t};\n}\n\n/** MANAGED tool: uv and Python share one deduplicated runtime manager with bounded provisioning. */\nasync function checkPython(deps: DoctorDeps, silent: boolean): Promise<DoctorCheck> {\n\tconst outcome = await deps.ensurePythonRuntime({ silent });\n\tif (outcome.status !== \"ready\") {\n\t\treturn {\n\t\t\tid: \"python\",\n\t\t\tlabel: \"Python (uv-managed)\",\n\t\t\tkind: \"managed\",\n\t\t\tpresent: false,\n\t\t\tdetail: outcome.reason,\n\t\t};\n\t}\n\treturn {\n\t\tid: \"python\",\n\t\tlabel: \"Python (uv-managed)\",\n\t\tkind: \"managed\",\n\t\tpresent: true,\n\t\tinstallAttempted: outcome.pythonInstalled,\n\t\tdetail: `uv: ${outcome.uvPath}; python: ${outcome.pythonPath}${outcome.pythonInstalled ? \" (installed just now)\" : \"\"}`,\n\t};\n}\n\nfunction checkAgentDirectoryLayout(deps: DoctorDeps): DoctorNotice[] {\n\tconst report = deps.inspectAgentDirectoryLayout();\n\tif (report.error) {\n\t\treturn [\n\t\t\t{\n\t\t\t\tid: \"agent-directory-layout\",\n\t\t\t\tlabel: \"Agent directory layout\",\n\t\t\t\tdetail: `could not inspect ${report.agentDir}: ${report.error}`,\n\t\t\t},\n\t\t];\n\t}\n\tif (report.unexpectedEntryCount === 0 && !report.truncated) return [];\n\n\tconst listed = report.unexpectedEntries.join(\", \");\n\tconst omitted = report.unexpectedEntryCount - report.unexpectedEntries.length;\n\tconst count = report.truncated ? `at least ${report.unexpectedEntryCount}` : String(report.unexpectedEntryCount);\n\tconst listing = listed ? `: ${listed}${omitted > 0 ? ` (+${omitted} more)` : \"\"}` : \"\";\n\tconst truncation = report.truncated ? `; scan stopped after ${report.scannedEntries} entries` : \"\";\n\treturn [\n\t\t{\n\t\t\tid: \"agent-directory-layout\",\n\t\t\tlabel: \"Agent directory layout\",\n\t\t\tdetail: `${count} unexpected root entr${report.unexpectedEntryCount === 1 ? \"y\" : \"ies\"}${listing}${truncation}. Extension data belongs under state/extensions, cache/extensions, or leased work/extensions`,\n\t\t},\n\t];\n}\n\nexport async function runDoctor(\n\tdeps: DoctorDeps = realDoctorDeps,\n\toptions: RunDoctorOptions = {},\n): Promise<DoctorReport> {\n\tconst silent = options.silent ?? true;\n\tconst [fffNode, ripgrep, jq, ollama, python] = await Promise.all([\n\t\tcheckFffNode(deps, silent),\n\t\tcheckManagedDataTool(deps, \"rg\", silent),\n\t\tcheckManagedDataTool(deps, \"jq\", silent),\n\t\tcheckOllama(deps),\n\t\tcheckPython(deps, silent),\n\t]);\n\treturn { checks: [fffNode, ripgrep, jq, ollama, python], notices: checkAgentDirectoryLayout(deps) };\n}\n\nexport function formatDoctorReport(report: DoctorReport): string {\n\tconst lines: string[] = [];\n\tfor (const check of report.checks) {\n\t\tconst status = check.present ? chalk.green(\"[OK]\") : chalk.yellow(\"[MISSING]\");\n\t\tconst detail = check.detail ? ` -- ${check.detail}` : \"\";\n\t\tlines.push(`${status} ${check.label}${detail}`);\n\t\tif (!check.present && check.guide) {\n\t\t\tfor (const guideLine of check.guide) lines.push(` ${guideLine}`);\n\t\t}\n\t}\n\tfor (const notice of report.notices ?? []) {\n\t\tlines.push(`${chalk.yellow(\"[WARN]\")} ${notice.label} -- ${notice.detail}`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Best-effort preflight meant to be called right after `pi-adaptative update`\n * succeeds. Must never fail the update itself: any error here is swallowed\n * and reported as a skipped check, not surfaced as an update failure.\n */\nexport async function runUpdatePreflight(deps: DoctorDeps = realDoctorDeps): Promise<void> {\n\ttry {\n\t\tconst report = await runDoctor(deps);\n\t\tconsole.log(`\\n${formatDoctorReport(report)}\\n`);\n\t} catch (error) {\n\t\tconsole.log(chalk.dim(`(environment check skipped: ${error instanceof Error ? error.message : String(error)})`));\n\t}\n}\n"]}
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/core/doctor.ts"],"names":[],"mappings":"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","sourcesContent":["import chalk from \"chalk\";\nimport { getAgentDir } from \"../config.ts\";\nimport {\n\tensureFffNodePackage,\n\tensureTool,\n\ttype FffInstallOutcome,\n\tgetLastFffInstallOutcome,\n\tloadAvailableFffNodePackage,\n\tprobeVersion,\n} from \"../utils/tools-manager.ts\";\nimport { type AgentDirectoryLayoutReport, inspectAgentDirectoryLayout } from \"./agent-directory-layout.ts\";\nimport { OllamaRuntime } from \"./models/local-runtime.ts\";\nimport { ensurePythonRuntime, type PythonRuntimeOutcome } from \"./python-runtime.ts\";\n\n/**\n * Environment doctor: verifies required tooling and installs what it safely\n * can, instead of leaving provisioning entirely to lazy first-use (the exact\n * gap behind \"ran `pi-adaptative update` on another machine and fff-node\n * never got installed\" -- see fff-lazy-install.test.ts/fff-search-tools.test.ts\n * for the underlying lazy-install fix; this module is the proactive half).\n *\n * Two tool kinds, two different postures:\n * - \"managed\": pi owns provisioning and attempts it when absent (fff-node,\n * pinned rg/jq/uv, and the Python interpreter resolved through uv).\n * - \"system\": pi does not own the install (Ollama). GUIDE MODE only -- exact\n * manual steps are reported, never executed.\n */\n\nexport type DoctorToolKind = \"managed\" | \"system\";\n\nexport interface DoctorCheck {\n\t/** Stable identifier, e.g. \"fff-node\", \"ripgrep\", \"jq\", \"ollama\", \"python\". */\n\tid: string;\n\t/** Human-readable label for the report. */\n\tlabel: string;\n\tkind: DoctorToolKind;\n\tpresent: boolean;\n\t/** Version/path/status summary, present or not. */\n\tdetail?: string;\n\t/** Only set for a \"managed\" tool: whether an install was just attempted. */\n\tinstallAttempted?: boolean;\n\t/** Only set for a \"system\" tool that's missing: exact manual steps, never executed. */\n\tguide?: string[];\n}\n\nexport interface DoctorReport {\n\tchecks: DoctorCheck[];\n\tnotices?: DoctorNotice[];\n}\n\nexport interface DoctorNotice {\n\tid: string;\n\tlabel: string;\n\tdetail: string;\n}\n\n/** Minimal slice of OllamaRuntime the doctor needs -- lets tests inject a fake without touching the real runtime. */\nexport type DoctorOllamaRuntime = Pick<OllamaRuntime, \"detect\" | \"installGuide\">;\n\nexport interface DoctorDeps {\n\tloadAvailableFffNodePackage: () => unknown | undefined;\n\tensureFffNodePackage: (silent?: boolean) => Promise<unknown | undefined>;\n\tgetLastFffInstallOutcome: () => FffInstallOutcome | undefined;\n\tensureTool: (tool: \"jq\" | \"jscpd\" | \"rg\", silent: boolean) => Promise<string | undefined>;\n\tensurePythonRuntime: (options: { silent: boolean }) => Promise<PythonRuntimeOutcome>;\n\t/** Best-effort `<command> --version` probe; undefined if it can't be run. Used only to enrich a status line, never for presence detection. */\n\tprobeVersion: (command: string, versionArgs?: readonly string[]) => string | undefined;\n\tollamaRuntime: DoctorOllamaRuntime;\n\tinspectAgentDirectoryLayout: () => AgentDirectoryLayoutReport;\n}\n\nexport interface RunDoctorOptions {\n\t/**\n\t * Whether the fff-node managed install (if one is attempted) stays quiet.\n\t * Default true, matching runUpdatePreflight's existing background\n\t * behavior. The interactive `doctor` command passes `false` so a\n\t * multi-second install doesn't read as a silent hang.\n\t */\n\tsilent?: boolean;\n}\n\nconst realDoctorDeps: DoctorDeps = {\n\tloadAvailableFffNodePackage,\n\tensureFffNodePackage,\n\tgetLastFffInstallOutcome,\n\tensureTool,\n\tensurePythonRuntime,\n\tprobeVersion,\n\tollamaRuntime: new OllamaRuntime({ agentDir: getAgentDir() }),\n\tinspectAgentDirectoryLayout: () => inspectAgentDirectoryLayout(getAgentDir()),\n};\n\nfunction describeFffOutcome(outcome: FffInstallOutcome | undefined): string {\n\tswitch (outcome?.status) {\n\t\tcase \"already-available\":\n\t\t\treturn \"already available\";\n\t\tcase \"installed\":\n\t\t\treturn \"installed just now\";\n\t\tcase \"offline\":\n\t\t\treturn \"offline mode enabled, skipped\";\n\t\tcase \"unsupported-platform\":\n\t\t\treturn \"no native build for this platform\";\n\t\tcase \"install-failed\":\n\t\t\treturn `install failed: ${outcome.reason}`;\n\t\tdefault:\n\t\t\treturn \"unavailable\";\n\t}\n}\n\n/** First line only -- a compiled-features/build-flags tail (e.g. rg --version) is noise in a one-line status report. */\nfunction firstLine(text: string | undefined): string | undefined {\n\treturn text?.split(\"\\n\")[0];\n}\n\n/** MANAGED tool: pi owns this install path, so the doctor actually attempts it when missing. */\nasync function checkFffNode(deps: DoctorDeps, silent: boolean): Promise<DoctorCheck> {\n\t// Delegate entirely to ensureFffNodePackage rather than re-deriving \"is it\n\t// already there\" here: that function (and its offline/unsupported-platform/\n\t// cooldown handling, see tools-manager.ts) is the single source of truth for\n\t// whether an install should even be attempted -- the doctor must not\n\t// duplicate or second-guess that logic, only report its outcome honestly.\n\tconst installed = await deps.ensureFffNodePackage(silent);\n\tconst outcome = deps.getLastFffInstallOutcome();\n\treturn {\n\t\tid: \"fff-node\",\n\t\tlabel: \"FFF native search (@ff-labs/fff-node)\",\n\t\tkind: \"managed\",\n\t\tpresent: Boolean(installed),\n\t\tinstallAttempted: outcome !== undefined && outcome.status !== \"already-available\",\n\t\tdetail: describeFffOutcome(outcome),\n\t};\n}\n\n/** MANAGED tools: exact releases and checksums are owned by tools-manager. */\nasync function checkManagedDataTool(\n\tdeps: DoctorDeps,\n\ttool: \"jq\" | \"jscpd\" | \"rg\",\n\tsilent: boolean,\n): Promise<DoctorCheck> {\n\tconst path = await deps.ensureTool(tool, silent);\n\tconst id = tool === \"rg\" ? \"ripgrep\" : tool;\n\tconst label =\n\t\ttool === \"rg\" ? \"ripgrep (rg)\" : tool === \"jscpd\" ? \"jscpd v5 (clone scanner)\" : \"jq (JSON projection)\";\n\tif (!path) return { id, label, kind: \"managed\", present: false, detail: \"managed install unavailable\" };\n\tconst version = firstLine(deps.probeVersion(path));\n\treturn {\n\t\tid,\n\t\tlabel,\n\t\tkind: \"managed\",\n\t\tpresent: true,\n\t\tdetail: version ? `${path} (${version})` : path,\n\t};\n}\n\n/** SYSTEM tool: guide mode only, never installed by the doctor (OllamaRuntime.installGuide() is itself already guide-mode-only). */\nasync function checkOllama(deps: DoctorDeps): Promise<DoctorCheck> {\n\tconst status = await deps.ollamaRuntime.detect();\n\tif (!status.binaryPath) {\n\t\treturn {\n\t\t\tid: \"ollama\",\n\t\t\tlabel: \"Ollama (local model runtime)\",\n\t\t\tkind: \"system\",\n\t\t\tpresent: false,\n\t\t\tguide: deps.ollamaRuntime.installGuide(),\n\t\t};\n\t}\n\tconst version = firstLine(deps.probeVersion(status.binaryPath));\n\tconst versionSuffix = version ? `, ${version}` : \"\";\n\tconst activeStore = status.activeStore\n\t\t? `${status.activeStore.path} [${status.activeStore.kind}, ${status.activeStore.modelCount} model(s)]`\n\t\t: `none; pi-owned store ${status.ownedModelsDir} has ${status.ownedStore.modelCount} model(s)`;\n\treturn {\n\t\tid: \"ollama\",\n\t\tlabel: \"Ollama (local model runtime)\",\n\t\tkind: \"system\",\n\t\tpresent: true,\n\t\tdetail: `binary: ${status.binaryPath} [${status.binarySource}]${versionSuffix}; server: ${status.serverUp ? \"up\" : \"down\"} at ${status.serverUrl}; active store: ${activeStore}`,\n\t};\n}\n\n/** MANAGED tool: uv and Python share one deduplicated runtime manager with bounded provisioning. */\nasync function checkPython(deps: DoctorDeps, silent: boolean): Promise<DoctorCheck> {\n\tconst outcome = await deps.ensurePythonRuntime({ silent });\n\tif (outcome.status !== \"ready\") {\n\t\treturn {\n\t\t\tid: \"python\",\n\t\t\tlabel: \"Python (uv-managed)\",\n\t\t\tkind: \"managed\",\n\t\t\tpresent: false,\n\t\t\tdetail: outcome.reason,\n\t\t};\n\t}\n\treturn {\n\t\tid: \"python\",\n\t\tlabel: \"Python (uv-managed)\",\n\t\tkind: \"managed\",\n\t\tpresent: true,\n\t\tinstallAttempted: outcome.pythonInstalled,\n\t\tdetail: `uv: ${outcome.uvPath}; python: ${outcome.pythonPath}${outcome.pythonInstalled ? \" (installed just now)\" : \"\"}`,\n\t};\n}\n\nfunction checkAgentDirectoryLayout(deps: DoctorDeps): DoctorNotice[] {\n\tconst report = deps.inspectAgentDirectoryLayout();\n\tif (report.error) {\n\t\treturn [\n\t\t\t{\n\t\t\t\tid: \"agent-directory-layout\",\n\t\t\t\tlabel: \"Agent directory layout\",\n\t\t\t\tdetail: `could not inspect ${report.agentDir}: ${report.error}`,\n\t\t\t},\n\t\t];\n\t}\n\tif (report.unexpectedEntryCount === 0 && !report.truncated) return [];\n\n\tconst listed = report.unexpectedEntries.join(\", \");\n\tconst omitted = report.unexpectedEntryCount - report.unexpectedEntries.length;\n\tconst count = report.truncated ? `at least ${report.unexpectedEntryCount}` : String(report.unexpectedEntryCount);\n\tconst listing = listed ? `: ${listed}${omitted > 0 ? ` (+${omitted} more)` : \"\"}` : \"\";\n\tconst truncation = report.truncated ? `; scan stopped after ${report.scannedEntries} entries` : \"\";\n\treturn [\n\t\t{\n\t\t\tid: \"agent-directory-layout\",\n\t\t\tlabel: \"Agent directory layout\",\n\t\t\tdetail: `${count} unexpected root entr${report.unexpectedEntryCount === 1 ? \"y\" : \"ies\"}${listing}${truncation}. Extension data belongs under state/extensions, cache/extensions, or leased work/extensions`,\n\t\t},\n\t];\n}\n\nexport async function runDoctor(\n\tdeps: DoctorDeps = realDoctorDeps,\n\toptions: RunDoctorOptions = {},\n): Promise<DoctorReport> {\n\tconst silent = options.silent ?? true;\n\tconst [fffNode, ripgrep, jq, jscpd, ollama, python] = await Promise.all([\n\t\tcheckFffNode(deps, silent),\n\t\tcheckManagedDataTool(deps, \"rg\", silent),\n\t\tcheckManagedDataTool(deps, \"jq\", silent),\n\t\tcheckManagedDataTool(deps, \"jscpd\", silent),\n\t\tcheckOllama(deps),\n\t\tcheckPython(deps, silent),\n\t]);\n\treturn { checks: [fffNode, ripgrep, jq, jscpd, ollama, python], notices: checkAgentDirectoryLayout(deps) };\n}\n\nexport function formatDoctorReport(report: DoctorReport): string {\n\tconst lines: string[] = [];\n\tfor (const check of report.checks) {\n\t\tconst status = check.present ? chalk.green(\"[OK]\") : chalk.yellow(\"[MISSING]\");\n\t\tconst detail = check.detail ? ` -- ${check.detail}` : \"\";\n\t\tlines.push(`${status} ${check.label}${detail}`);\n\t\tif (!check.present && check.guide) {\n\t\t\tfor (const guideLine of check.guide) lines.push(` ${guideLine}`);\n\t\t}\n\t}\n\tfor (const notice of report.notices ?? []) {\n\t\tlines.push(`${chalk.yellow(\"[WARN]\")} ${notice.label} -- ${notice.detail}`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Best-effort preflight meant to be called right after `pi-adaptative update`\n * succeeds. Must never fail the update itself: any error here is swallowed\n * and reported as a skipped check, not surfaced as an update failure.\n */\nexport async function runUpdatePreflight(deps: DoctorDeps = realDoctorDeps): Promise<void> {\n\ttry {\n\t\tconst report = await runDoctor(deps);\n\t\tconsole.log(`\\n${formatDoctorReport(report)}\\n`);\n\t} catch (error) {\n\t\tconsole.log(chalk.dim(`(environment check skipped: ${error instanceof Error ? error.message : String(error)})`));\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"model-capability.d.ts","sourceRoot":"","sources":["../../src/core/model-capability.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;AAExE,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,KAAK,GAAG,oBAAoB,CAAC;AAExE,MAAM,WAAW,sBAAsB;IACtC,KAAK,EAAE,oBAAoB,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,0FAA0F;IAC1F,sBAAsB,EAAE,OAAO,CAAC;IAChC,4EAA4E;IAC5E,mBAAmB,EAAE,MAAM,CAAC;CAC5B;AAED,8DAA8D;AAC9D,eAAO,MAAM,iCAAiC,QAAS,CAAC;AACxD,oFAAoF;AACpF,eAAO,MAAM,iCAAiC,QAAS,CAAC;AACxD,+EAA+E;AAC/E,eAAO,MAAM,oCAAoC,OAAQ,CAAC;AAE1D,eAAO,MAAM,mCAAmC,EAAE,SAAS,MAAM,EAWhE,CAAC;AACF,eAAO,MAAM,sCAAsC,EAAE,SAAS,MAAM,EAUnE,CAAC;AACF,eAAO,MAAM,mCAAmC,EAAE,SAAS,MAAM,EAAO,CAAC;AAEzE,eAAO,MAAM,8BAA8B,OAAO,CAAC;AA0CnD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE;IAClD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,mBAAmB,CAAC;CAC3B,GAAG,sBAAsB,CA2BzB;AAED,6FAA6F;AAC7F,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,sBAAsB,GAAG,MAAM,EAAE,CAWpH;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,uBAAuB,GAChC,6BAA6B,GAC7B,wBAAwB,GACxB,2BAA2B,GAC3B,sBAAsB,CAAC;AAE1B,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,uBAAuB,CAAC;IAChC,eAAe,EAAE,oBAAoB,CAAC;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAC/C,eAAe,EAAE,oBAAoB,CAAC;IACtC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,qBAAqB,EAAE,OAAO,CAAC;IAC/B,kBAAkB,EAAE,OAAO,CAAC;CAC5B,GAAG,iBAAiB,GAAG,SAAS,CAOhC;AAED,6EAA6E;AAC7E,eAAO,MAAM,0BAA0B,uCAAuC,CAAC;AAE/E,0GAA0G;AAC1G,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAI5F"}
1
+ {"version":3,"file":"model-capability.d.ts","sourceRoot":"","sources":["../../src/core/model-capability.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;AAExE,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,KAAK,GAAG,oBAAoB,CAAC;AAExE,MAAM,WAAW,sBAAsB;IACtC,KAAK,EAAE,oBAAoB,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,0FAA0F;IAC1F,sBAAsB,EAAE,OAAO,CAAC;IAChC,4EAA4E;IAC5E,mBAAmB,EAAE,MAAM,CAAC;CAC5B;AAED,8DAA8D;AAC9D,eAAO,MAAM,iCAAiC,QAAS,CAAC;AACxD,oFAAoF;AACpF,eAAO,MAAM,iCAAiC,QAAS,CAAC;AACxD,+EAA+E;AAC/E,eAAO,MAAM,oCAAoC,OAAQ,CAAC;AAE1D,eAAO,MAAM,mCAAmC,EAAE,SAAS,MAAM,EAYhE,CAAC;AACF,eAAO,MAAM,sCAAsC,EAAE,SAAS,MAAM,EAUnE,CAAC;AACF,eAAO,MAAM,mCAAmC,EAAE,SAAS,MAAM,EAAO,CAAC;AAEzE,eAAO,MAAM,8BAA8B,OAAO,CAAC;AA0CnD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE;IAClD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,mBAAmB,CAAC;CAC3B,GAAG,sBAAsB,CA2BzB;AAED,6FAA6F;AAC7F,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,sBAAsB,GAAG,MAAM,EAAE,CAWpH;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,uBAAuB,GAChC,6BAA6B,GAC7B,wBAAwB,GACxB,2BAA2B,GAC3B,sBAAsB,CAAC;AAE1B,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,uBAAuB,CAAC;IAChC,eAAe,EAAE,oBAAoB,CAAC;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAC/C,eAAe,EAAE,oBAAoB,CAAC;IACtC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,qBAAqB,EAAE,OAAO,CAAC;IAC/B,kBAAkB,EAAE,OAAO,CAAC;CAC5B,GAAG,iBAAiB,GAAG,SAAS,CAOhC;AAED,6EAA6E;AAC7E,eAAO,MAAM,0BAA0B,uCAAuC,CAAC;AAE/E,0GAA0G;AAC1G,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAI5F"}
@@ -15,6 +15,7 @@ export const MODEL_CAPABILITY_LEAN_MIN_CONTEXT = 16_384;
15
15
  export const MODEL_CAPABILITY_MINIMAL_MIN_CONTEXT = 8_192;
16
16
  export const MODEL_CAPABILITY_LEAN_BLOCKED_TOOLS = [
17
17
  "delegate",
18
+ "profile_writer",
18
19
  "context_audit",
19
20
  "goal",
20
21
  "worktree_sync",
@@ -1 +1 @@
1
- {"version":3,"file":"model-capability.js","sourceRoot":"","sources":["../../src/core/model-capability.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Model capability auto-detection: derive what the harness may load onto a model FROM the model's\n * own metadata (`Model.contextWindow`), so small open models (4k/8k/16k windows, sub-1B params)\n * can still hold a usable chat instead of drowning in tool schemas and background-lane prompts.\n *\n * Derivation is metadata-first; defaults apply only when the metadata is missing (unknown/zero\n * window keeps today's full behavior rather than guessing). Detection can be disabled or forced\n * per class via the `modelCapability.mode` setting.\n */\n\nexport type ModelCapabilityClass = \"full\" | \"lean\" | \"minimal\" | \"chat\";\n\nexport type ModelCapabilityMode = \"auto\" | \"off\" | ModelCapabilityClass;\n\nexport interface ModelCapabilityProfile {\n\tclass: ModelCapabilityClass;\n\tcontextWindow?: number;\n\treasonCode: string;\n\t/** Allow-list; undefined = no allow-list restriction. */\n\tallowedToolNames?: readonly string[];\n\t/** Block-list applied after the allow-list; undefined = nothing blocked. */\n\tblockedToolNames?: readonly string[];\n\t/** Whether idle background lanes (goal auto-continue, research) may run on this model. */\n\tbackgroundLanesEnabled: boolean;\n\t/** Output-token cap for lane isolated completions, scaled to the window. */\n\tlaneMaxOutputTokens: number;\n}\n\n/** Windows at or above this keep the full harness surface. */\nexport const MODEL_CAPABILITY_FULL_MIN_CONTEXT = 32_768;\n/** Windows at or above this keep core tools but shed background-autonomy extras. */\nexport const MODEL_CAPABILITY_LEAN_MIN_CONTEXT = 16_384;\n/** Windows at or above this get the minimal coding set; below is chat-only. */\nexport const MODEL_CAPABILITY_MINIMAL_MIN_CONTEXT = 8_192;\n\nexport const MODEL_CAPABILITY_LEAN_BLOCKED_TOOLS: readonly string[] = [\n\t\"delegate\",\n\t\"context_audit\",\n\t\"goal\",\n\t\"worktree_sync\",\n\t\"improvement_loop\",\n\t\"extensionify\",\n\t\"skillify\",\n\t\"model_fitness\",\n\t\"context_scout\",\n\t\"tmux_agent_manager\",\n];\nexport const MODEL_CAPABILITY_MINIMAL_ALLOWED_TOOLS: readonly string[] = [\n\t\"read\",\n\t\"bash\",\n\t\"python\",\n\t\"powershell\",\n\t\"edit\",\n\t\"write\",\n\t\"ask_question\",\n\t// The executor tool: minimal-class models ARE the daily-ops executors, and its schema is tiny.\n\t\"run_toolkit_script\",\n];\nexport const MODEL_CAPABILITY_CHAT_ALLOWED_TOOLS: readonly string[] = [];\n\nexport const DEFAULT_LANE_MAX_OUTPUT_TOKENS = 2048;\nconst MIN_LANE_MAX_OUTPUT_TOKENS = 256;\n\nfunction laneOutputTokensForWindow(contextWindow: number | undefined): number {\n\tif (contextWindow === undefined || contextWindow <= 0) return DEFAULT_LANE_MAX_OUTPUT_TOKENS;\n\t// A lane completion may use at most an eighth of the window for output, floored so tiny\n\t// windows still produce something parseable.\n\treturn Math.min(DEFAULT_LANE_MAX_OUTPUT_TOKENS, Math.max(MIN_LANE_MAX_OUTPUT_TOKENS, Math.floor(contextWindow / 8)));\n}\n\nfunction profileForClass(\n\tcapabilityClass: ModelCapabilityClass,\n\treasonCode: string,\n\tcontextWindow: number | undefined,\n): ModelCapabilityProfile {\n\tconst base = {\n\t\tclass: capabilityClass,\n\t\treasonCode,\n\t\tbackgroundLanesEnabled: true,\n\t\tlaneMaxOutputTokens: laneOutputTokensForWindow(contextWindow),\n\t\t...(contextWindow !== undefined && contextWindow > 0 ? { contextWindow } : {}),\n\t};\n\tswitch (capabilityClass) {\n\t\tcase \"full\":\n\t\t\treturn base;\n\t\tcase \"lean\":\n\t\t\treturn { ...base, blockedToolNames: MODEL_CAPABILITY_LEAN_BLOCKED_TOOLS };\n\t\tcase \"minimal\":\n\t\t\treturn {\n\t\t\t\t...base,\n\t\t\t\tallowedToolNames: MODEL_CAPABILITY_MINIMAL_ALLOWED_TOOLS,\n\t\t\t\tbackgroundLanesEnabled: false,\n\t\t\t};\n\t\tcase \"chat\":\n\t\t\treturn {\n\t\t\t\t...base,\n\t\t\t\tallowedToolNames: MODEL_CAPABILITY_CHAT_ALLOWED_TOOLS,\n\t\t\t\tbackgroundLanesEnabled: false,\n\t\t\t};\n\t}\n}\n\nexport function deriveModelCapabilityProfile(args: {\n\tcontextWindow?: number;\n\tmode?: ModelCapabilityMode;\n}): ModelCapabilityProfile {\n\tconst mode = args.mode ?? \"auto\";\n\tconst contextWindow =\n\t\targs.contextWindow !== undefined && Number.isFinite(args.contextWindow) && args.contextWindow > 0\n\t\t\t? args.contextWindow\n\t\t\t: undefined;\n\tif (mode === \"off\") {\n\t\treturn profileForClass(\"full\", \"detection_disabled\", contextWindow);\n\t}\n\tif (mode !== \"auto\") {\n\t\treturn profileForClass(mode, \"forced_by_setting\", contextWindow);\n\t}\n\n\tif (contextWindow === undefined) {\n\t\t// Metadata missing: defaults, never guesses.\n\t\treturn profileForClass(\"full\", \"unknown_context_window_defaults\", undefined);\n\t}\n\tif (contextWindow >= MODEL_CAPABILITY_FULL_MIN_CONTEXT) {\n\t\treturn profileForClass(\"full\", \"large_context_window\", contextWindow);\n\t}\n\tif (contextWindow >= MODEL_CAPABILITY_LEAN_MIN_CONTEXT) {\n\t\treturn profileForClass(\"lean\", \"lean_context_window\", contextWindow);\n\t}\n\tif (contextWindow >= MODEL_CAPABILITY_MINIMAL_MIN_CONTEXT) {\n\t\treturn profileForClass(\"minimal\", \"minimal_context_window\", contextWindow);\n\t}\n\treturn profileForClass(\"chat\", \"chat_only_context_window\", contextWindow);\n}\n\n/** Apply the profile's allow/block lists to a requested tool-name list, preserving order. */\nexport function filterToolNamesForCapability(toolNames: readonly string[], profile: ModelCapabilityProfile): string[] {\n\tlet filtered = [...toolNames];\n\tif (profile.allowedToolNames !== undefined) {\n\t\tconst allowed = new Set(profile.allowedToolNames);\n\t\tfiltered = filtered.filter((name) => allowed.has(name));\n\t}\n\tif (profile.blockedToolNames !== undefined) {\n\t\tconst blocked = new Set(profile.blockedToolNames);\n\t\tfiltered = filtered.filter((name) => !blocked.has(name));\n\t}\n\treturn filtered;\n}\n\n/**\n * Lane-worker eligibility: a session bound to a worktree-sync lane (`--worktree-lane` /\n * `PI_WORKTREE_LANE`, see worktree-sync/runtime.ts) is expected to drive the FULL multi-step\n * lane-gate/recovery surface -- sync, conflict recovery, land -- unattended. A sub-full capability\n * class or a model with no working native tool-call path cannot reliably drive that surface. This\n * rides the SAME capability system every other adaptation in this file rides (class + context\n * window + the `/toolprobe` verdict) -- no parallel mechanism, no new env var, no new registry field.\n */\nexport type LaneWorkerRefusalReason =\n\t| \"capability_class_below_full\"\n\t| \"context_window_unknown\"\n\t| \"tool_calling_unadvertised\"\n\t| \"tool_calling_demoted\";\n\nexport interface LaneWorkerRefusal {\n\treason: LaneWorkerRefusalReason;\n\tcapabilityClass: ModelCapabilityClass;\n\tcontextWindow?: number;\n}\n\n/**\n * Decide whether the model described by `args` may drive a worktree-sync lane worker. First\n * failure wins, in order: capability class below full; an unknown/undeclared context window (the\n * classifier's own registry-derived SSOT -- see `deriveModelCapabilityProfile`; a full class can\n * still carry an undefined window via the `unknown_context_window_defaults` fallback); no\n * ADVERTISED native tool-call path (`Model.textToolCallProtocol` unset/false -- set true means\n * phone-only); or a GRADED `/toolprobe` demotion to \"text-protocol\"/\"none\". An UNPROBED model (no\n * verdict on record yet) is eligible on its advertised support alone -- unprobed is never treated\n * as demoted. `undefined` means eligible.\n */\nexport function evaluateLaneWorkerRefusal(args: {\n\tcapabilityClass: ModelCapabilityClass;\n\tcontextWindow: number | undefined;\n\ttoolCallingAdvertised: boolean;\n\ttoolCallingDemoted: boolean;\n}): LaneWorkerRefusal | undefined {\n\tconst { capabilityClass, contextWindow, toolCallingAdvertised, toolCallingDemoted } = args;\n\tif (capabilityClass !== \"full\") return { reason: \"capability_class_below_full\", capabilityClass, contextWindow };\n\tif (contextWindow === undefined) return { reason: \"context_window_unknown\", capabilityClass, contextWindow };\n\tif (!toolCallingAdvertised) return { reason: \"tool_calling_unadvertised\", capabilityClass, contextWindow };\n\tif (toolCallingDemoted) return { reason: \"tool_calling_demoted\", capabilityClass, contextWindow };\n\treturn undefined;\n}\n\n/** Stable, greppable prefix for {@link formatLaneWorkerRefusal}'s output. */\nexport const LANE_WORKER_REFUSAL_PREFIX = \"worktree-sync lane-worker refusal:\";\n\n/** Format a refusal into one deterministic, greppable line naming the lane, class, window, and reason. */\nexport function formatLaneWorkerRefusal(refusal: LaneWorkerRefusal, laneKey?: string): string {\n\tconst laneSuffix = laneKey !== undefined ? ` lane=${laneKey}` : \"\";\n\tconst windowText = refusal.contextWindow !== undefined ? String(refusal.contextWindow) : \"unknown\";\n\treturn `${LANE_WORKER_REFUSAL_PREFIX}${laneSuffix} class=${refusal.capabilityClass} contextWindow=${windowText} reason=${refusal.reason}`;\n}\n"]}
1
+ {"version":3,"file":"model-capability.js","sourceRoot":"","sources":["../../src/core/model-capability.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Model capability auto-detection: derive what the harness may load onto a model FROM the model's\n * own metadata (`Model.contextWindow`), so small open models (4k/8k/16k windows, sub-1B params)\n * can still hold a usable chat instead of drowning in tool schemas and background-lane prompts.\n *\n * Derivation is metadata-first; defaults apply only when the metadata is missing (unknown/zero\n * window keeps today's full behavior rather than guessing). Detection can be disabled or forced\n * per class via the `modelCapability.mode` setting.\n */\n\nexport type ModelCapabilityClass = \"full\" | \"lean\" | \"minimal\" | \"chat\";\n\nexport type ModelCapabilityMode = \"auto\" | \"off\" | ModelCapabilityClass;\n\nexport interface ModelCapabilityProfile {\n\tclass: ModelCapabilityClass;\n\tcontextWindow?: number;\n\treasonCode: string;\n\t/** Allow-list; undefined = no allow-list restriction. */\n\tallowedToolNames?: readonly string[];\n\t/** Block-list applied after the allow-list; undefined = nothing blocked. */\n\tblockedToolNames?: readonly string[];\n\t/** Whether idle background lanes (goal auto-continue, research) may run on this model. */\n\tbackgroundLanesEnabled: boolean;\n\t/** Output-token cap for lane isolated completions, scaled to the window. */\n\tlaneMaxOutputTokens: number;\n}\n\n/** Windows at or above this keep the full harness surface. */\nexport const MODEL_CAPABILITY_FULL_MIN_CONTEXT = 32_768;\n/** Windows at or above this keep core tools but shed background-autonomy extras. */\nexport const MODEL_CAPABILITY_LEAN_MIN_CONTEXT = 16_384;\n/** Windows at or above this get the minimal coding set; below is chat-only. */\nexport const MODEL_CAPABILITY_MINIMAL_MIN_CONTEXT = 8_192;\n\nexport const MODEL_CAPABILITY_LEAN_BLOCKED_TOOLS: readonly string[] = [\n\t\"delegate\",\n\t\"profile_writer\",\n\t\"context_audit\",\n\t\"goal\",\n\t\"worktree_sync\",\n\t\"improvement_loop\",\n\t\"extensionify\",\n\t\"skillify\",\n\t\"model_fitness\",\n\t\"context_scout\",\n\t\"tmux_agent_manager\",\n];\nexport const MODEL_CAPABILITY_MINIMAL_ALLOWED_TOOLS: readonly string[] = [\n\t\"read\",\n\t\"bash\",\n\t\"python\",\n\t\"powershell\",\n\t\"edit\",\n\t\"write\",\n\t\"ask_question\",\n\t// The executor tool: minimal-class models ARE the daily-ops executors, and its schema is tiny.\n\t\"run_toolkit_script\",\n];\nexport const MODEL_CAPABILITY_CHAT_ALLOWED_TOOLS: readonly string[] = [];\n\nexport const DEFAULT_LANE_MAX_OUTPUT_TOKENS = 2048;\nconst MIN_LANE_MAX_OUTPUT_TOKENS = 256;\n\nfunction laneOutputTokensForWindow(contextWindow: number | undefined): number {\n\tif (contextWindow === undefined || contextWindow <= 0) return DEFAULT_LANE_MAX_OUTPUT_TOKENS;\n\t// A lane completion may use at most an eighth of the window for output, floored so tiny\n\t// windows still produce something parseable.\n\treturn Math.min(DEFAULT_LANE_MAX_OUTPUT_TOKENS, Math.max(MIN_LANE_MAX_OUTPUT_TOKENS, Math.floor(contextWindow / 8)));\n}\n\nfunction profileForClass(\n\tcapabilityClass: ModelCapabilityClass,\n\treasonCode: string,\n\tcontextWindow: number | undefined,\n): ModelCapabilityProfile {\n\tconst base = {\n\t\tclass: capabilityClass,\n\t\treasonCode,\n\t\tbackgroundLanesEnabled: true,\n\t\tlaneMaxOutputTokens: laneOutputTokensForWindow(contextWindow),\n\t\t...(contextWindow !== undefined && contextWindow > 0 ? { contextWindow } : {}),\n\t};\n\tswitch (capabilityClass) {\n\t\tcase \"full\":\n\t\t\treturn base;\n\t\tcase \"lean\":\n\t\t\treturn { ...base, blockedToolNames: MODEL_CAPABILITY_LEAN_BLOCKED_TOOLS };\n\t\tcase \"minimal\":\n\t\t\treturn {\n\t\t\t\t...base,\n\t\t\t\tallowedToolNames: MODEL_CAPABILITY_MINIMAL_ALLOWED_TOOLS,\n\t\t\t\tbackgroundLanesEnabled: false,\n\t\t\t};\n\t\tcase \"chat\":\n\t\t\treturn {\n\t\t\t\t...base,\n\t\t\t\tallowedToolNames: MODEL_CAPABILITY_CHAT_ALLOWED_TOOLS,\n\t\t\t\tbackgroundLanesEnabled: false,\n\t\t\t};\n\t}\n}\n\nexport function deriveModelCapabilityProfile(args: {\n\tcontextWindow?: number;\n\tmode?: ModelCapabilityMode;\n}): ModelCapabilityProfile {\n\tconst mode = args.mode ?? \"auto\";\n\tconst contextWindow =\n\t\targs.contextWindow !== undefined && Number.isFinite(args.contextWindow) && args.contextWindow > 0\n\t\t\t? args.contextWindow\n\t\t\t: undefined;\n\tif (mode === \"off\") {\n\t\treturn profileForClass(\"full\", \"detection_disabled\", contextWindow);\n\t}\n\tif (mode !== \"auto\") {\n\t\treturn profileForClass(mode, \"forced_by_setting\", contextWindow);\n\t}\n\n\tif (contextWindow === undefined) {\n\t\t// Metadata missing: defaults, never guesses.\n\t\treturn profileForClass(\"full\", \"unknown_context_window_defaults\", undefined);\n\t}\n\tif (contextWindow >= MODEL_CAPABILITY_FULL_MIN_CONTEXT) {\n\t\treturn profileForClass(\"full\", \"large_context_window\", contextWindow);\n\t}\n\tif (contextWindow >= MODEL_CAPABILITY_LEAN_MIN_CONTEXT) {\n\t\treturn profileForClass(\"lean\", \"lean_context_window\", contextWindow);\n\t}\n\tif (contextWindow >= MODEL_CAPABILITY_MINIMAL_MIN_CONTEXT) {\n\t\treturn profileForClass(\"minimal\", \"minimal_context_window\", contextWindow);\n\t}\n\treturn profileForClass(\"chat\", \"chat_only_context_window\", contextWindow);\n}\n\n/** Apply the profile's allow/block lists to a requested tool-name list, preserving order. */\nexport function filterToolNamesForCapability(toolNames: readonly string[], profile: ModelCapabilityProfile): string[] {\n\tlet filtered = [...toolNames];\n\tif (profile.allowedToolNames !== undefined) {\n\t\tconst allowed = new Set(profile.allowedToolNames);\n\t\tfiltered = filtered.filter((name) => allowed.has(name));\n\t}\n\tif (profile.blockedToolNames !== undefined) {\n\t\tconst blocked = new Set(profile.blockedToolNames);\n\t\tfiltered = filtered.filter((name) => !blocked.has(name));\n\t}\n\treturn filtered;\n}\n\n/**\n * Lane-worker eligibility: a session bound to a worktree-sync lane (`--worktree-lane` /\n * `PI_WORKTREE_LANE`, see worktree-sync/runtime.ts) is expected to drive the FULL multi-step\n * lane-gate/recovery surface -- sync, conflict recovery, land -- unattended. A sub-full capability\n * class or a model with no working native tool-call path cannot reliably drive that surface. This\n * rides the SAME capability system every other adaptation in this file rides (class + context\n * window + the `/toolprobe` verdict) -- no parallel mechanism, no new env var, no new registry field.\n */\nexport type LaneWorkerRefusalReason =\n\t| \"capability_class_below_full\"\n\t| \"context_window_unknown\"\n\t| \"tool_calling_unadvertised\"\n\t| \"tool_calling_demoted\";\n\nexport interface LaneWorkerRefusal {\n\treason: LaneWorkerRefusalReason;\n\tcapabilityClass: ModelCapabilityClass;\n\tcontextWindow?: number;\n}\n\n/**\n * Decide whether the model described by `args` may drive a worktree-sync lane worker. First\n * failure wins, in order: capability class below full; an unknown/undeclared context window (the\n * classifier's own registry-derived SSOT -- see `deriveModelCapabilityProfile`; a full class can\n * still carry an undefined window via the `unknown_context_window_defaults` fallback); no\n * ADVERTISED native tool-call path (`Model.textToolCallProtocol` unset/false -- set true means\n * phone-only); or a GRADED `/toolprobe` demotion to \"text-protocol\"/\"none\". An UNPROBED model (no\n * verdict on record yet) is eligible on its advertised support alone -- unprobed is never treated\n * as demoted. `undefined` means eligible.\n */\nexport function evaluateLaneWorkerRefusal(args: {\n\tcapabilityClass: ModelCapabilityClass;\n\tcontextWindow: number | undefined;\n\ttoolCallingAdvertised: boolean;\n\ttoolCallingDemoted: boolean;\n}): LaneWorkerRefusal | undefined {\n\tconst { capabilityClass, contextWindow, toolCallingAdvertised, toolCallingDemoted } = args;\n\tif (capabilityClass !== \"full\") return { reason: \"capability_class_below_full\", capabilityClass, contextWindow };\n\tif (contextWindow === undefined) return { reason: \"context_window_unknown\", capabilityClass, contextWindow };\n\tif (!toolCallingAdvertised) return { reason: \"tool_calling_unadvertised\", capabilityClass, contextWindow };\n\tif (toolCallingDemoted) return { reason: \"tool_calling_demoted\", capabilityClass, contextWindow };\n\treturn undefined;\n}\n\n/** Stable, greppable prefix for {@link formatLaneWorkerRefusal}'s output. */\nexport const LANE_WORKER_REFUSAL_PREFIX = \"worktree-sync lane-worker refusal:\";\n\n/** Format a refusal into one deterministic, greppable line naming the lane, class, window, and reason. */\nexport function formatLaneWorkerRefusal(refusal: LaneWorkerRefusal, laneKey?: string): string {\n\tconst laneSuffix = laneKey !== undefined ? ` lane=${laneKey}` : \"\";\n\tconst windowText = refusal.contextWindow !== undefined ? String(refusal.contextWindow) : \"unknown\";\n\treturn `${LANE_WORKER_REFUSAL_PREFIX}${laneSuffix} class=${refusal.capabilityClass} contextWindow=${windowText} reason=${refusal.reason}`;\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import type { OrchestrationProfile, ToolCapabilityManifest } from "./contracts.ts";
2
2
  export declare const CLASSIFIED_LANE_TOOL_NAMES: readonly ["read", "grep", "find", "ls", "write", "edit", "memory", "run_process"];
3
- export declare const ORCHESTRATION_PROFILE_TOOL_NAMES: readonly ["read", "grep", "find", "ls", "write", "edit", "memory", "run_process", "delegate", "delegate_status"];
3
+ export declare const ORCHESTRATION_PROFILE_TOOL_NAMES: readonly ["read", "grep", "find", "ls", "write", "edit", "memory", "run_process", "delegate", "delegate_status", "profile_writer"];
4
4
  /**
5
5
  * Manifest-only lane catalogue. It contains no tool factories, so policy compilation can omit
6
6
  * denied tools before their executable implementations are constructed.
@@ -1 +1 @@
1
- {"version":3,"file":"lane-tool-manifests.d.ts","sourceRoot":"","sources":["../../../src/core/orchestration/lane-tool-manifests.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAEnF,eAAO,MAAM,0BAA0B,YACtC,MAAM,EACN,MAAM,EACN,MAAM,EACN,IAAI,EACJ,OAAO,EACP,MAAM,EACN,QAAQ,EACR,aAAa,CACJ,CAAC;AACX,eAAO,MAAM,gCAAgC,kHAA0E,CAAC;AAExH;;;GAGG;AACH,wBAAgB,sBAAsB,CACrC,OAAO,EAAE,oBAAoB,EAC7B,gBAAgB,EAAE,SAAS,MAAM,EAAE,GACjC,sBAAsB,EAAE,CAkB1B"}
1
+ {"version":3,"file":"lane-tool-manifests.d.ts","sourceRoot":"","sources":["../../../src/core/orchestration/lane-tool-manifests.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAEnF,eAAO,MAAM,0BAA0B,YACtC,MAAM,EACN,MAAM,EACN,MAAM,EACN,IAAI,EACJ,OAAO,EACP,MAAM,EACN,QAAQ,EACR,aAAa,CACJ,CAAC;AACX,eAAO,MAAM,gCAAgC,oIAKnC,CAAC;AAEX;;;GAGG;AACH,wBAAgB,sBAAsB,CACrC,OAAO,EAAE,oBAAoB,EAC7B,gBAAgB,EAAE,SAAS,MAAM,EAAE,GACjC,sBAAsB,EAAE,CAkB1B"}
@@ -9,7 +9,12 @@ export const CLASSIFIED_LANE_TOOL_NAMES = [
9
9
  "memory",
10
10
  "run_process",
11
11
  ];
12
- export const ORCHESTRATION_PROFILE_TOOL_NAMES = [...CLASSIFIED_LANE_TOOL_NAMES, "delegate", "delegate_status"];
12
+ export const ORCHESTRATION_PROFILE_TOOL_NAMES = [
13
+ ...CLASSIFIED_LANE_TOOL_NAMES,
14
+ "delegate",
15
+ "delegate_status",
16
+ "profile_writer",
17
+ ];
13
18
  /**
14
19
  * Manifest-only lane catalogue. It contains no tool factories, so policy compilation can omit
15
20
  * denied tools before their executable implementations are constructed.
@@ -1 +1 @@
1
- {"version":3,"file":"lane-tool-manifests.js","sourceRoot":"","sources":["../../../src/core/orchestration/lane-tool-manifests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AAGrG,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACzC,MAAM;IACN,MAAM;IACN,MAAM;IACN,IAAI;IACJ,OAAO;IACP,MAAM;IACN,QAAQ;IACR,aAAa;CACJ,CAAC;AACX,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,GAAG,0BAA0B,EAAE,UAAU,EAAE,iBAAiB,CAAU,CAAC;AAExH;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACrC,OAA6B,EAC7B,gBAAmC;IAEnC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC1C,MAAM,SAAS,GAA6B,EAAE,CAAC;IAE/C,KAAK,MAAM,QAAQ,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QAC1C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,SAAS;QACrC,MAAM,MAAM,GAAG,uBAAuB,CAAC,QAAQ,CAAC,CAAC;QACjD,MAAM,UAAU,GAAG,4BAA4B,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACnE,IAAI,CAAC,UAAU,IAAI,CAAC,MAAM;YAAE,SAAS;QACrC,SAAS,CAAC,IAAI,CAAC;YACd,QAAQ;YACR,eAAe,EAAE,YAAY,QAAQ,KAAK;YAC1C,YAAY,EAAE,CAAC,UAAU,CAAC;YAC1B,KAAK,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;YACrB,YAAY,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC;SAClC,CAAC,CAAC;IACJ,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC","sourcesContent":["import { getToolCapabilityPolicy, resolveProfileToolCapability } from \"../tool-capability-policy.ts\";\nimport type { OrchestrationProfile, ToolCapabilityManifest } from \"./contracts.ts\";\n\nexport const CLASSIFIED_LANE_TOOL_NAMES = [\n\t\"read\",\n\t\"grep\",\n\t\"find\",\n\t\"ls\",\n\t\"write\",\n\t\"edit\",\n\t\"memory\",\n\t\"run_process\",\n] as const;\nexport const ORCHESTRATION_PROFILE_TOOL_NAMES = [...CLASSIFIED_LANE_TOOL_NAMES, \"delegate\", \"delegate_status\"] as const;\n\n/**\n * Manifest-only lane catalogue. It contains no tool factories, so policy compilation can omit\n * denied tools before their executable implementations are constructed.\n */\nexport function buildLaneToolManifests(\n\tprofile: OrchestrationProfile,\n\tenabledToolNames: readonly string[],\n): ToolCapabilityManifest[] {\n\tconst enabled = new Set(enabledToolNames);\n\tconst manifests: ToolCapabilityManifest[] = [];\n\n\tfor (const toolName of profile.toolNames) {\n\t\tif (!enabled.has(toolName)) continue;\n\t\tconst policy = getToolCapabilityPolicy(toolName);\n\t\tconst capability = resolveProfileToolCapability(profile, toolName);\n\t\tif (!capability || !policy) continue;\n\t\tmanifests.push({\n\t\t\ttoolName,\n\t\t\tmoduleSpecifier: `../tools/${toolName}.ts`,\n\t\t\tcapabilities: [capability],\n\t\t\troles: [profile.role],\n\t\t\tenforcements: [policy.enforcement],\n\t\t});\n\t}\n\treturn manifests;\n}\n"]}
1
+ {"version":3,"file":"lane-tool-manifests.js","sourceRoot":"","sources":["../../../src/core/orchestration/lane-tool-manifests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AAGrG,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACzC,MAAM;IACN,MAAM;IACN,MAAM;IACN,IAAI;IACJ,OAAO;IACP,MAAM;IACN,QAAQ;IACR,aAAa;CACJ,CAAC;AACX,MAAM,CAAC,MAAM,gCAAgC,GAAG;IAC/C,GAAG,0BAA0B;IAC7B,UAAU;IACV,iBAAiB;IACjB,gBAAgB;CACP,CAAC;AAEX;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACrC,OAA6B,EAC7B,gBAAmC;IAEnC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC1C,MAAM,SAAS,GAA6B,EAAE,CAAC;IAE/C,KAAK,MAAM,QAAQ,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QAC1C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,SAAS;QACrC,MAAM,MAAM,GAAG,uBAAuB,CAAC,QAAQ,CAAC,CAAC;QACjD,MAAM,UAAU,GAAG,4BAA4B,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACnE,IAAI,CAAC,UAAU,IAAI,CAAC,MAAM;YAAE,SAAS;QACrC,SAAS,CAAC,IAAI,CAAC;YACd,QAAQ;YACR,eAAe,EAAE,YAAY,QAAQ,KAAK;YAC1C,YAAY,EAAE,CAAC,UAAU,CAAC;YAC1B,KAAK,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;YACrB,YAAY,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC;SAClC,CAAC,CAAC;IACJ,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC","sourcesContent":["import { getToolCapabilityPolicy, resolveProfileToolCapability } from \"../tool-capability-policy.ts\";\nimport type { OrchestrationProfile, ToolCapabilityManifest } from \"./contracts.ts\";\n\nexport const CLASSIFIED_LANE_TOOL_NAMES = [\n\t\"read\",\n\t\"grep\",\n\t\"find\",\n\t\"ls\",\n\t\"write\",\n\t\"edit\",\n\t\"memory\",\n\t\"run_process\",\n] as const;\nexport const ORCHESTRATION_PROFILE_TOOL_NAMES = [\n\t...CLASSIFIED_LANE_TOOL_NAMES,\n\t\"delegate\",\n\t\"delegate_status\",\n\t\"profile_writer\",\n] as const;\n\n/**\n * Manifest-only lane catalogue. It contains no tool factories, so policy compilation can omit\n * denied tools before their executable implementations are constructed.\n */\nexport function buildLaneToolManifests(\n\tprofile: OrchestrationProfile,\n\tenabledToolNames: readonly string[],\n): ToolCapabilityManifest[] {\n\tconst enabled = new Set(enabledToolNames);\n\tconst manifests: ToolCapabilityManifest[] = [];\n\n\tfor (const toolName of profile.toolNames) {\n\t\tif (!enabled.has(toolName)) continue;\n\t\tconst policy = getToolCapabilityPolicy(toolName);\n\t\tconst capability = resolveProfileToolCapability(profile, toolName);\n\t\tif (!capability || !policy) continue;\n\t\tmanifests.push({\n\t\t\ttoolName,\n\t\t\tmoduleSpecifier: `../tools/${toolName}.ts`,\n\t\t\tcapabilities: [capability],\n\t\t\troles: [profile.role],\n\t\t\tenforcements: [policy.enforcement],\n\t\t});\n\t}\n\treturn manifests;\n}\n"]}
@@ -0,0 +1,31 @@
1
+ import type { SessionManager } from "@caupulican/pi-agent-core/node";
2
+ import type { OrchestrationProfile } from "./contracts.ts";
3
+ export declare const SESSION_TASK_PROFILE_CUSTOM_TYPE = "session_task_profile";
4
+ export declare const MAX_SESSION_TASK_PROFILES = 32;
5
+ export interface SessionTaskProfileRecord {
6
+ baseProfileId: string;
7
+ authorProfileId?: string;
8
+ profile: OrchestrationProfile;
9
+ }
10
+ export interface SessionTaskProfileLoadResult {
11
+ records: SessionTaskProfileRecord[];
12
+ registry: ReadonlyMap<string, SessionTaskProfileRecord>;
13
+ diagnostics: string[];
14
+ }
15
+ /**
16
+ * Append-only task-profile storage on the active session branch.
17
+ *
18
+ * The common append path walks only entries added since the cached leaf. Branching away from the
19
+ * cached leaf deliberately rebuilds from parent links so abandoned task profiles cannot leak into
20
+ * the active branch.
21
+ */
22
+ export declare class SessionTaskProfileStore {
23
+ private readonly sessionManager;
24
+ private cachedLeafId;
25
+ private cachedResult;
26
+ constructor(sessionManager: SessionManager);
27
+ load(): SessionTaskProfileLoadResult;
28
+ append(input: SessionTaskProfileRecord): SessionTaskProfileRecord;
29
+ private cloneResult;
30
+ }
31
+ //# sourceMappingURL=session-task-profile-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-task-profile-store.d.ts","sourceRoot":"","sources":["../../../src/core/orchestration/session-task-profile-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACnF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAG3D,eAAO,MAAM,gCAAgC,yBAAyB,CAAC;AACvE,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAG5C,MAAM,WAAW,wBAAwB;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,oBAAoB,CAAC;CAC9B;AAED,MAAM,WAAW,4BAA4B;IAC5C,OAAO,EAAE,wBAAwB,EAAE,CAAC;IACpC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;IACxD,WAAW,EAAE,MAAM,EAAE,CAAC;CACtB;AAoCD;;;;;;GAMG;AACH,qBAAa,uBAAuB;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,YAAY,CAIlB;IAEF,YAAY,cAAc,EAAE,cAAc,EAEzC;IAED,IAAI,IAAI,4BAA4B,CAqDnC;IAED,MAAM,CAAC,KAAK,EAAE,wBAAwB,GAAG,wBAAwB,CAiChE;IAED,OAAO,CAAC,WAAW;CAQnB"}