@selesai/code 0.13.12 → 0.13.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/dist/extensions/pi-subagents/CHANGELOG.md +77 -1
- package/dist/extensions/pi-subagents/VISION.md +12 -1
- package/dist/extensions/pi-subagents/docs/agents.md +13 -6
- package/dist/extensions/pi-subagents/docs/configuration.md +35 -9
- package/dist/extensions/pi-subagents/docs/extension-api.md +1 -1
- package/dist/extensions/pi-subagents/docs/missions.md +1 -1
- package/dist/extensions/pi-subagents/docs/models.md +6 -6
- package/dist/extensions/pi-subagents/docs/observability.md +6 -3
- package/dist/extensions/pi-subagents/docs/tool-reference.md +3 -3
- package/dist/extensions/pi-subagents/docs/watchdog.md +92 -114
- package/dist/extensions/pi-subagents/docs/workflows.md +1 -1
- package/dist/extensions/pi-subagents/install.mjs +1 -2
- package/dist/extensions/pi-subagents/package.json +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +6 -6
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +4 -4
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/review-and-validation.md +1 -1
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +41 -4
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +3 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +120 -124
- package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +5 -1
- package/dist/extensions/pi-subagents/src/api/preflight.ts +4 -0
- package/dist/extensions/pi-subagents/src/api/shared-types.ts +3 -0
- package/dist/extensions/pi-subagents/src/extension/config.ts +20 -0
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +1 -0
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +6 -2
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +1 -1
- package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +19 -13
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +26 -8
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +103 -23
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +6 -2
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -2
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +54 -3
- package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +16 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +22 -2
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +63 -6
- package/dist/extensions/pi-subagents/src/runs/background/steering.ts +4 -1
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +69 -12
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +13 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +21 -8
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +59 -6
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +95 -18
- package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +11 -43
- package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +1 -0
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/lane-metadata.ts +24 -3
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +2 -6
- package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +1 -0
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +33 -18
- package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +73 -37
- package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +33 -6
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +4 -2
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +20 -3
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +21 -7
- package/dist/extensions/pi-subagents/src/runs/shared/tool-timeout.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +467 -63
- package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +3 -1
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +0 -12
- package/dist/extensions/pi-subagents/src/shared/fork-session-cwd.ts +27 -0
- package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +3 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +42 -4
- package/dist/extensions/pi-subagents/src/shared/utils.ts +18 -7
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +1 -1
- package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +26 -12
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +61 -2
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +12 -7
- package/dist/extensions/pi-subagents/src/tui/render.ts +227 -14
- package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +56 -34
- package/dist/extensions/pi-subagents/src/watchdog/diff-tool.ts +77 -0
- package/dist/extensions/pi-subagents/src/watchdog/emission-guard.ts +5 -3
- package/dist/extensions/pi-subagents/src/watchdog/guidance.ts +20 -0
- package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +16 -13
- package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +10 -9
- package/dist/extensions/pi-subagents/src/watchdog/render.ts +4 -5
- package/dist/extensions/pi-subagents/src/watchdog/review.ts +15 -4
- package/dist/extensions/pi-subagents/src/watchdog/rules.ts +70 -0
- package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +75 -92
- package/dist/extensions/pi-subagents/src/watchdog/scope.ts +2 -1
- package/dist/extensions/pi-subagents/src/watchdog/settings.ts +48 -104
- package/dist/extensions/pi-subagents/src/watchdog/types.ts +18 -32
- package/dist/extensions/pi-subagents/src/watchdog/warning-format.ts +0 -1
- package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +3 -2
- package/dist/extensions/pi-subagents/src/workflows/workflow-checklist.ts +439 -0
- package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +28 -1
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +335 -21
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +4 -1
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +22 -18
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +0 -1
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +21 -18
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +307 -22
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/support/helpers.ts +25 -0
- package/dist/extensions/pi-subagents/test/support/isolated-temp-root.mjs +15 -4
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +17 -2
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +96 -15
- package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +53 -5
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +20 -0
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +221 -8
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +45 -24
- package/dist/extensions/pi-subagents/test/unit/agent-scan-dirs.test.ts +133 -0
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +28 -2
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +2 -0
- package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +4 -1
- package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +18 -4
- package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +16 -0
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +38 -0
- package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +19 -6
- package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +26 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +22 -1
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +2 -6
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +66 -1
- package/dist/extensions/pi-subagents/test/unit/pi-args-permission-system.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +116 -10
- package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +136 -18
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +20 -0
- package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +22 -0
- package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/project-panes-public-api.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +127 -48
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +68 -0
- package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +17 -0
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +88 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +4 -2
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/steering.test.ts +9 -3
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +22 -9
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +91 -8
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/temp-paths.test.ts +50 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +0 -1
- package/dist/extensions/pi-subagents/test/unit/tool-timeout.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +1 -2
- package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +73 -13
- package/dist/extensions/pi-subagents/test/unit/watchdog-diff-tool.test.ts +79 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-permission-arbiter.test.ts +2 -4
- package/dist/extensions/pi-subagents/test/unit/watchdog-render.test.ts +7 -8
- package/dist/extensions/pi-subagents/test/unit/watchdog-review.test.ts +48 -3
- package/dist/extensions/pi-subagents/test/unit/watchdog-rules.test.ts +37 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +36 -118
- package/dist/extensions/pi-subagents/test/unit/watchdog-scope.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +37 -14
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +45 -26
- package/dist/extensions/pi-subagents/test/unit/workflow-checklist.test.ts +223 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +50 -1
- package/dist/extensions/pi-subagents/test/unit/workflow-preflight.test.ts +22 -0
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +136 -4
- package/dist/extensions/pi-zentui/README.md +19 -5
- package/dist/extensions/pi-zentui/docs/configuration.md +37 -9
- package/dist/extensions/pi-zentui/extensions/zentui/config.ts +29 -0
- package/dist/extensions/pi-zentui/extensions/zentui/index.ts +31 -2
- package/dist/extensions/pi-zentui/extensions/zentui/prototype-patch-registry.ts +55 -11
- package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +115 -3
- package/dist/extensions/pi-zentui/extensions/zentui/settings-previews.ts +119 -2
- package/dist/extensions/pi-zentui/extensions/zentui/thinking-experimental.ts +1768 -0
- package/dist/extensions/pi-zentui/extensions/zentui/thinking-status.ts +69 -0
- package/dist/extensions/pi-zentui/extensions/zentui/thinking-steps.ts +155 -0
- package/dist/extensions/pi-zentui/extensions/zentui/ui.ts +2 -1
- package/dist/extensions/pi-zentui/extensions/zentui/user-message-osc.ts +16 -2
- package/dist/extensions/pi-zentui/extensions/zentui/user-message-styles.ts +1 -1
- package/dist/extensions/pi-zentui/test/config-load-lifecycle.test.ts +124 -0
- package/dist/extensions/pi-zentui/test/config.test.ts +82 -2
- package/dist/extensions/pi-zentui/test/editor-viewport-indicators.test.ts +19 -4
- package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +58 -9
- package/dist/extensions/pi-zentui/test/package-contents.mjs +33 -0
- package/dist/extensions/pi-zentui/test/prototype-patch-registry.test.ts +131 -0
- package/dist/extensions/pi-zentui/test/responsive-dependencies.test.ts +4 -4
- package/dist/extensions/pi-zentui/test/settings-command.test.ts +289 -7
- package/dist/extensions/pi-zentui/test/settings-previews.test.ts +106 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-docs.test.ts +51 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-loader-diagnostics.mjs +54 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-missing-export.test.ts +44 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-rows.test.ts +297 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-tui.mjs +2329 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental.test.ts +1668 -0
- package/dist/extensions/pi-zentui/test/thinking-status.test.ts +85 -0
- package/dist/extensions/pi-zentui/test/thinking-steps.test.ts +122 -0
- package/dist/extensions/pi-zentui/test/user-message-osc.test.ts +58 -0
- package/dist/skills/brandkit/SKILL.md +0 -2
- package/dist/skills/{industrial-brutalist-ui → brutalist}/SKILL.md +1 -3
- package/dist/skills/gpt-taste/SKILL.md +0 -2
- package/dist/skills/image-to-code/SKILL.md +0 -2
- package/dist/skills/imagegen-frontend-mobile/SKILL.md +0 -2
- package/dist/skills/imagegen-frontend-web/SKILL.md +0 -2
- package/dist/skills/{minimalist-ui → minimalist}/SKILL.md +1 -3
- package/dist/skills/{full-output-enforcement → output}/SKILL.md +1 -3
- package/dist/skills/{redesign-existing-projects → redesign}/SKILL.md +1 -3
- package/dist/skills/{high-end-visual-design → soft}/SKILL.md +1 -3
- package/dist/skills/stitch/DESIGN.md +121 -0
- package/dist/skills/{stitch-design-taste → stitch}/SKILL.md +1 -3
- package/dist/skills/{design-taste-frontend → taste}/SKILL.md +1 -3
- package/dist/skills/{design-taste-frontend-v1 → taste-v1}/SKILL.md +2 -4
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AsyncStatus, WorkflowLaneMetadata, WorkflowLaneMode } from "../../shared/types.ts";
|
|
1
|
+
import type { AsyncStatus, ManagedWorktreeProvider, WorktreeNaming, WorkflowLaneMetadata, WorkflowLaneMode } from "../../shared/types.ts";
|
|
2
2
|
|
|
3
3
|
export const WORKFLOW_LANE_KEY_MAX_BYTES = 128;
|
|
4
4
|
export const WORKFLOW_LANE_SOURCE_REF_MAX_BYTES = 128;
|
|
@@ -8,6 +8,7 @@ export const WORKFLOW_LANE_OUTPUT_PATH_MAX_BYTES = 256;
|
|
|
8
8
|
export const WORKFLOW_LANE_OUTPUT_PATHS_MAX = 10;
|
|
9
9
|
export const WORKTREE_STATUS_PATH_MAX_BYTES = 4096;
|
|
10
10
|
export const WORKTREE_STATUS_BRANCH_MAX_BYTES = 256;
|
|
11
|
+
export const WORKTREE_STATUS_NAMING_LABEL_MAX_BYTES = 256;
|
|
11
12
|
|
|
12
13
|
const WORKFLOW_LANE_KEY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
13
14
|
const WORKFLOW_LANE_MODES = new Set<WorkflowLaneMode>(["mutation", "review", "scout", "gate"]);
|
|
@@ -75,16 +76,36 @@ export function assertWorkflowLaneKey(lane: WorkflowLaneMetadata | undefined, wo
|
|
|
75
76
|
export interface WorktreeStatusReference {
|
|
76
77
|
worktreePath: string;
|
|
77
78
|
branch: string;
|
|
79
|
+
provider?: ManagedWorktreeProvider;
|
|
80
|
+
naming?: WorktreeNaming;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function normalizeWorktreeNaming(value: unknown, label: string): WorktreeNaming {
|
|
84
|
+
assertPlainObject(value, label);
|
|
85
|
+
assertKnownFields(value, ["requestedBranch", "branchPrefix", "label", "sanitizedPathComponent", "collision", "collisionSuffix"], label);
|
|
86
|
+
const collision = value.collision;
|
|
87
|
+
if (collision !== undefined && collision !== "branch" && collision !== "path" && collision !== "both") throw new Error(`${label}.collision is invalid.`);
|
|
88
|
+
return {
|
|
89
|
+
requestedBranch: boundedNonEmptyString(value.requestedBranch, `${label}.requestedBranch`, WORKTREE_STATUS_BRANCH_MAX_BYTES),
|
|
90
|
+
branchPrefix: boundedNonEmptyString(value.branchPrefix, `${label}.branchPrefix`, WORKTREE_STATUS_BRANCH_MAX_BYTES),
|
|
91
|
+
label: boundedNonEmptyString(value.label, `${label}.label`, WORKTREE_STATUS_NAMING_LABEL_MAX_BYTES),
|
|
92
|
+
sanitizedPathComponent: boundedNonEmptyString(value.sanitizedPathComponent, `${label}.sanitizedPathComponent`, WORKTREE_STATUS_NAMING_LABEL_MAX_BYTES),
|
|
93
|
+
...(collision !== undefined ? { collision } : {}),
|
|
94
|
+
...(value.collisionSuffix !== undefined ? { collisionSuffix: boundedNonEmptyString(value.collisionSuffix, `${label}.collisionSuffix`, WORKTREE_STATUS_NAMING_LABEL_MAX_BYTES) } : {}),
|
|
95
|
+
};
|
|
78
96
|
}
|
|
79
97
|
|
|
80
98
|
/** Validate the display-only worktree fields copied into status.json. */
|
|
81
99
|
export function normalizeWorktreeStatusReference(value: unknown, label = "worktree status reference"): WorktreeStatusReference | undefined {
|
|
82
100
|
if (value === undefined) return undefined;
|
|
83
101
|
assertPlainObject(value, label);
|
|
84
|
-
assertKnownFields(value, ["worktreePath", "branch"], label);
|
|
102
|
+
assertKnownFields(value, ["worktreePath", "branch", "provider", "naming"], label);
|
|
103
|
+
if (value.provider !== undefined && value.provider !== "native" && value.provider !== "worktrunk") throw new Error(`${label}.provider is invalid.`);
|
|
85
104
|
return {
|
|
86
105
|
worktreePath: boundedNonEmptyString(value.worktreePath, `${label}.worktreePath`, WORKTREE_STATUS_PATH_MAX_BYTES),
|
|
87
106
|
branch: boundedNonEmptyString(value.branch, `${label}.branch`, WORKTREE_STATUS_BRANCH_MAX_BYTES),
|
|
107
|
+
...(value.provider !== undefined ? { provider: value.provider as ManagedWorktreeProvider } : {}),
|
|
108
|
+
...(value.naming !== undefined ? { naming: normalizeWorktreeNaming(value.naming, `${label}.naming`) } : {}),
|
|
88
109
|
};
|
|
89
110
|
}
|
|
90
111
|
|
|
@@ -100,6 +121,6 @@ export function validateAsyncStatusLaneMetadata(status: Pick<AsyncStatus, "runId
|
|
|
100
121
|
const hasPath = step.worktreePath !== undefined;
|
|
101
122
|
const hasBranch = step.branch !== undefined;
|
|
102
123
|
if (hasPath !== hasBranch) throw new Error(`${label}.steps[${index}] must include both worktreePath and branch.`);
|
|
103
|
-
if (hasPath) normalizeWorktreeStatusReference({ worktreePath: step.worktreePath, branch: step.branch }, `${label}.steps[${index}]`);
|
|
124
|
+
if (hasPath) normalizeWorktreeStatusReference({ worktreePath: step.worktreePath, branch: step.branch, ...(step.provider ? { provider: step.provider } : {}), ...(step.naming ? { naming: step.naming } : {}) }, `${label}.steps[${index}]`);
|
|
104
125
|
}
|
|
105
126
|
}
|
|
@@ -574,6 +574,8 @@ export function writeParallelHandoffGroup(input: {
|
|
|
574
574
|
index: worktree.index,
|
|
575
575
|
path: worktree.path,
|
|
576
576
|
branch: worktree.branch,
|
|
577
|
+
...(worktree.provider ? { provider: worktree.provider } : {}),
|
|
578
|
+
...(worktree.naming ? { naming: worktree.naming } : {}),
|
|
577
579
|
worktreeRemoved: false,
|
|
578
580
|
branchRemoved: false,
|
|
579
581
|
preserved: true,
|
|
@@ -653,6 +655,8 @@ export function discardPreservedWorktrees(
|
|
|
653
655
|
path: task.path,
|
|
654
656
|
agentCwd: task.path,
|
|
655
657
|
branch: task.branch,
|
|
658
|
+
...(task.provider ? { provider: task.provider } : {}),
|
|
659
|
+
...(task.naming ? { naming: task.naming } : {}),
|
|
656
660
|
index: task.index,
|
|
657
661
|
nodeModulesLinked: false,
|
|
658
662
|
syntheticPaths: [],
|
|
@@ -42,6 +42,7 @@ export interface RunnerSubagentStep {
|
|
|
42
42
|
skipPrimaryModelVerification?: boolean;
|
|
43
43
|
modelVerificationRegistry?: Array<{ provider: string; id: string; fullId: string; contextWindow?: number }>;
|
|
44
44
|
tools?: string[];
|
|
45
|
+
excludeTools?: string[];
|
|
45
46
|
allowNestedSubagents?: boolean;
|
|
46
47
|
extensions?: string[];
|
|
47
48
|
subagentOnlyExtensions?: string[];
|
|
@@ -68,12 +69,7 @@ export interface RunnerSubagentStep {
|
|
|
68
69
|
toolTimeoutMs?: number;
|
|
69
70
|
waitToolEnabled?: boolean;
|
|
70
71
|
waitToolDefaultTimeoutMs?: number;
|
|
71
|
-
structuredOutput?:
|
|
72
|
-
schema: import("../../shared/types.ts").JsonSchemaObject;
|
|
73
|
-
schemaPath: string;
|
|
74
|
-
outputPath: string;
|
|
75
|
-
acceptanceReportPath?: string;
|
|
76
|
-
};
|
|
72
|
+
structuredOutput?: import("./structured-output.ts").StructuredOutputRuntime;
|
|
77
73
|
structuredOutputSchema?: import("../../shared/types.ts").JsonSchemaObject;
|
|
78
74
|
agentContract?: import("../../shared/types.ts").AgentContract;
|
|
79
75
|
definitionDigest?: string;
|
|
@@ -8,6 +8,7 @@ export interface PermissionConfig { rules?: PermissionRules }
|
|
|
8
8
|
export const PERMISSION_POLICY_ENV = "SELESAI_SUBAGENT_PERMISSION_POLICY";
|
|
9
9
|
export const PERMISSION_AUDIT_PATH_ENV = "SELESAI_SUBAGENT_PERMISSION_AUDIT_PATH";
|
|
10
10
|
const INTERNAL_TOOLS = new Set(["contact_supervisor", "intercom", "bg_wait", "subagent_wait", "structured_output"]);
|
|
11
|
+
|
|
11
12
|
const DECISIONS = new Set<PermissionDecision>(["allow", "ask", "deny"]);
|
|
12
13
|
const MAX_POLICY_BYTES = 16 * 1024;
|
|
13
14
|
const MAX_PREVIEW_BYTES = 2048;
|
|
@@ -20,8 +20,10 @@ import { encodeExtensionBindings, SELESAI_SUBAGENT_EXTENSION_BINDINGS_ENV, type
|
|
|
20
20
|
import { RUNTIME_EXTENSION_ACK_PATH_ENV } from "./runtime-acknowledged-extensions.ts";
|
|
21
21
|
import {
|
|
22
22
|
STRUCTURED_OUTPUT_ACCEPTANCE_CAPTURE_ENV,
|
|
23
|
+
STRUCTURED_OUTPUT_ACCEPTANCE_REQUIRED_ENV,
|
|
23
24
|
STRUCTURED_OUTPUT_CAPTURE_ENV,
|
|
24
25
|
STRUCTURED_OUTPUT_SCHEMA_ENV,
|
|
26
|
+
type StructuredOutputRuntime,
|
|
25
27
|
} from "./structured-output.ts";
|
|
26
28
|
import {
|
|
27
29
|
TEMP_ROOT_DIR,
|
|
@@ -45,7 +47,6 @@ import {
|
|
|
45
47
|
} from "./tool-availability.ts";
|
|
46
48
|
import {
|
|
47
49
|
CHILD_WATCHDOG_CONFIG_ENV,
|
|
48
|
-
encodeChildWatchdogConfig,
|
|
49
50
|
type ChildWatchdogConfig,
|
|
50
51
|
} from "../../watchdog/child-status.ts";
|
|
51
52
|
import { WAIT_TOOL_DEFAULT_TIMEOUT_MS_ENV, WAIT_TOOL_ENABLED_ENV } from "../background/wait-config.ts";
|
|
@@ -91,11 +92,12 @@ export function resolveSubagentTaskDelivery(
|
|
|
91
92
|
: "auto";
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
function shouldDeliverTaskViaFile(
|
|
95
|
+
export function shouldDeliverTaskViaFile(
|
|
95
96
|
task: string,
|
|
96
97
|
delivery: SubagentTaskDelivery,
|
|
98
|
+
platform: NodeJS.Platform = process.platform,
|
|
97
99
|
): boolean {
|
|
98
|
-
return delivery === "file" || task.length > TASK_ARG_LIMIT;
|
|
100
|
+
return delivery === "file" || platform === "darwin" || task.length > TASK_ARG_LIMIT;
|
|
99
101
|
}
|
|
100
102
|
const MAX_LAUNCH_RESOLVED_EXTENSION_IDS = 32;
|
|
101
103
|
const PROMPT_RUNTIME_EXTENSION_PATH = path.join(
|
|
@@ -171,6 +173,7 @@ export interface BuildPiArgsInput {
|
|
|
171
173
|
inheritSkills: boolean;
|
|
172
174
|
requireReadTool?: boolean;
|
|
173
175
|
tools?: string[];
|
|
176
|
+
excludeTools?: string[];
|
|
174
177
|
extensions?: string[];
|
|
175
178
|
subagentOnlyExtensions?: string[];
|
|
176
179
|
systemPrompt?: string | null;
|
|
@@ -201,12 +204,7 @@ export interface BuildPiArgsInput {
|
|
|
201
204
|
steerInboxDir?: string;
|
|
202
205
|
steerCapabilityPath?: string;
|
|
203
206
|
steerAckDir?: string;
|
|
204
|
-
structuredOutput?:
|
|
205
|
-
schema: JsonSchemaObject;
|
|
206
|
-
schemaPath: string;
|
|
207
|
-
outputPath: string;
|
|
208
|
-
acceptanceReportPath?: string;
|
|
209
|
-
};
|
|
207
|
+
structuredOutput?: StructuredOutputRuntime;
|
|
210
208
|
fast?: boolean;
|
|
211
209
|
modelCandidates?: readonly string[];
|
|
212
210
|
toolBudget?: ResolvedToolBudget;
|
|
@@ -300,6 +298,7 @@ function resolveFastModeExtension(input: Pick<ResolvePiLaunchToolPlanInput, "fas
|
|
|
300
298
|
|
|
301
299
|
export interface ResolvePiLaunchToolPlanInput {
|
|
302
300
|
tools?: string[];
|
|
301
|
+
excludeTools?: string[];
|
|
303
302
|
allowNestedSubagents?: boolean;
|
|
304
303
|
extensions?: string[];
|
|
305
304
|
subagentOnlyExtensions?: string[];
|
|
@@ -329,6 +328,7 @@ export interface PiLaunchToolPlan {
|
|
|
329
328
|
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
330
329
|
requestedBuiltinTools: string[];
|
|
331
330
|
declaredBuiltinTools: string[];
|
|
331
|
+
excludeTools: string[];
|
|
332
332
|
toolExtensionPaths: string[];
|
|
333
333
|
resolvedMcpSelections: ResolvedMcpDirectToolSelection[];
|
|
334
334
|
effectiveMcpSelections: ResolvedMcpDirectToolSelection[];
|
|
@@ -499,8 +499,12 @@ export function resolvePiLaunchToolPlan(
|
|
|
499
499
|
? ["read", ...requestedBuiltinTools]
|
|
500
500
|
: requestedBuiltinTools
|
|
501
501
|
).filter((tool) => !allowedToolSet || allowedToolSet.has(tool));
|
|
502
|
-
const
|
|
502
|
+
const excludeTools = [...new Set((input.excludeTools ?? []).map((tool) => tool.trim()).filter(Boolean))];
|
|
503
|
+
const excludedToolSet = new Set(excludeTools);
|
|
504
|
+
const effectiveDeclaredBuiltinTools = declaredBuiltinTools.filter((tool) => !excludedToolSet.has(tool));
|
|
505
|
+
const fanoutAuthorized = effectiveDeclaredBuiltinTools.includes("subagent") || (
|
|
503
506
|
input.allowNestedSubagents === true &&
|
|
507
|
+
!excludedToolSet.has("subagent") &&
|
|
504
508
|
(!allowedToolSet || allowedToolSet.has("subagent"))
|
|
505
509
|
);
|
|
506
510
|
const toolExtensionPaths: string[] = capabilityCeiling?.denyExtensions
|
|
@@ -524,7 +528,7 @@ export function resolvePiLaunchToolPlan(
|
|
|
524
528
|
!allowedToolSet ||
|
|
525
529
|
allowedToolSet.has(selection.name) ||
|
|
526
530
|
isLegacyUnderscoreMcpToolAllowed(selection, allowedToolSet, resolvedMcpNames, legacyMcpNameCounts),
|
|
527
|
-
);
|
|
531
|
+
).filter((selection) => !excludedToolSet.has(selection.name));
|
|
528
532
|
const effectiveMcpTools = effectiveMcpSelections.map(
|
|
529
533
|
(selection) => selection.name,
|
|
530
534
|
);
|
|
@@ -540,10 +544,10 @@ export function resolvePiLaunchToolPlan(
|
|
|
540
544
|
input.tools !== undefined ||
|
|
541
545
|
(input.mcpDirectTools?.length ?? 0) > 0 ||
|
|
542
546
|
allowedToolSet !== undefined;
|
|
543
|
-
const internalTools = input.structuredOutput ? ["structured_output"] : [];
|
|
547
|
+
const internalTools = (input.structuredOutput ? ["structured_output"] : []).filter((tool) => !excludedToolSet.has(tool));
|
|
544
548
|
const effectiveToolAllowlist = [
|
|
545
549
|
...new Set([
|
|
546
|
-
...
|
|
550
|
+
...effectiveDeclaredBuiltinTools,
|
|
547
551
|
...effectiveMcpTools,
|
|
548
552
|
...internalTools,
|
|
549
553
|
]),
|
|
@@ -554,11 +558,11 @@ export function resolvePiLaunchToolPlan(
|
|
|
554
558
|
// appended intercom alongside contact_supervisor, so that exact pairing is
|
|
555
559
|
// legacy plumbing, not a user demand for an external intercom provider;
|
|
556
560
|
// a lone intercom entry stays strictly required (#1207).
|
|
557
|
-
const legacySupervisorPairing =
|
|
561
|
+
const legacySupervisorPairing = effectiveDeclaredBuiltinTools.includes("contact_supervisor");
|
|
558
562
|
const requiredChildTools = explicitToolAllowlist
|
|
559
563
|
? [
|
|
560
564
|
...new Set([
|
|
561
|
-
...(input.tools !== undefined ?
|
|
565
|
+
...(input.tools !== undefined ? effectiveDeclaredBuiltinTools : []),
|
|
562
566
|
...(input.mcpDirectTools?.length ? effectiveMcpTools : []),
|
|
563
567
|
...internalTools,
|
|
564
568
|
].filter((tool) => tool !== "contact_supervisor" && (!legacySupervisorPairing || tool !== "intercom"))),
|
|
@@ -620,6 +624,7 @@ export function resolvePiLaunchToolPlan(
|
|
|
620
624
|
ceiling: capabilityCeiling,
|
|
621
625
|
...(requestedToolNames ? { requestedTools: requestedToolNames } : {}),
|
|
622
626
|
effectiveTools: effectiveToolAllowlist,
|
|
627
|
+
...(excludeTools.length > 0 ? { excludeTools } : {}),
|
|
623
628
|
removedTools:
|
|
624
629
|
requestedToolNames?.filter(
|
|
625
630
|
(tool) => !effectiveToolAllowlist.includes(tool),
|
|
@@ -657,6 +662,7 @@ export function resolvePiLaunchToolPlan(
|
|
|
657
662
|
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
658
663
|
requestedBuiltinTools,
|
|
659
664
|
declaredBuiltinTools,
|
|
665
|
+
excludeTools,
|
|
660
666
|
toolExtensionPaths,
|
|
661
667
|
resolvedMcpSelections,
|
|
662
668
|
effectiveMcpSelections,
|
|
@@ -740,6 +746,7 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
|
|
|
740
746
|
|
|
741
747
|
const toolPlan = resolvePiLaunchToolPlan({
|
|
742
748
|
tools: input.tools,
|
|
749
|
+
excludeTools: input.excludeTools,
|
|
743
750
|
allowNestedSubagents: input.allowNestedSubagents,
|
|
744
751
|
extensions: input.extensions,
|
|
745
752
|
subagentOnlyExtensions: input.subagentOnlyExtensions,
|
|
@@ -766,6 +773,8 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
|
|
|
766
773
|
);
|
|
767
774
|
if (toolPlan.effectiveToolAllowlist.length > 0)
|
|
768
775
|
args.push(toolPlan.effectiveToolAllowlist.join(","));
|
|
776
|
+
} else if (toolPlan.excludeTools.length > 0) {
|
|
777
|
+
args.push("--exclude-tools", toolPlan.excludeTools.join(","));
|
|
769
778
|
}
|
|
770
779
|
if (toolPlan.disableAmbientExtensions) {
|
|
771
780
|
args.push("--no-extensions");
|
|
@@ -836,6 +845,11 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
|
|
|
836
845
|
);
|
|
837
846
|
env[RUNTIME_EXTENSION_ACK_PATH_ENV] = runtimeAcknowledgedExtensionsPath;
|
|
838
847
|
let toolDiagnosticPath: string | undefined;
|
|
848
|
+
// Child launch environments are merged over process.env. Explicitly clear
|
|
849
|
+
// parent-scoped diagnostics so a nested zero-tool child cannot validate
|
|
850
|
+
// against, or overwrite, its parent's required-tool report.
|
|
851
|
+
env[REQUIRED_CHILD_TOOLS_ENV] = undefined;
|
|
852
|
+
env[CHILD_TOOL_DIAGNOSTIC_PATH_ENV] = undefined;
|
|
839
853
|
if (toolPlan.requiredChildTools.length > 0) {
|
|
840
854
|
if (!tempDir)
|
|
841
855
|
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
|
|
@@ -999,10 +1013,13 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
|
|
|
999
1013
|
env[SUBAGENT_CAPABILITY_CEILING_ENV] = encodedCapabilityCeiling;
|
|
1000
1014
|
if (encodedPermissionRules)
|
|
1001
1015
|
env[PERMISSION_POLICY_ENV] = encodedPermissionRules;
|
|
1016
|
+
env[STRUCTURED_OUTPUT_ACCEPTANCE_CAPTURE_ENV] = undefined;
|
|
1017
|
+
env[STRUCTURED_OUTPUT_ACCEPTANCE_REQUIRED_ENV] = undefined;
|
|
1002
1018
|
if (input.structuredOutput) {
|
|
1003
1019
|
env[STRUCTURED_OUTPUT_CAPTURE_ENV] = input.structuredOutput.outputPath;
|
|
1004
1020
|
env[STRUCTURED_OUTPUT_SCHEMA_ENV] = input.structuredOutput.schemaPath;
|
|
1005
1021
|
if (input.structuredOutput.acceptanceReportPath) env[STRUCTURED_OUTPUT_ACCEPTANCE_CAPTURE_ENV] = input.structuredOutput.acceptanceReportPath;
|
|
1022
|
+
if (input.structuredOutput.acceptanceReportRequired) env[STRUCTURED_OUTPUT_ACCEPTANCE_REQUIRED_ENV] = "1";
|
|
1006
1023
|
}
|
|
1007
1024
|
if (input.steerInboxDir) {
|
|
1008
1025
|
env[SUBAGENT_STEER_INBOX_ENV] = input.steerInboxDir;
|
|
@@ -1013,9 +1030,7 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
|
|
|
1013
1030
|
const encodedToolBudget = encodeToolBudgetEnv(input.toolBudget);
|
|
1014
1031
|
if (encodedToolBudget) env[TOOL_BUDGET_ENV] = encodedToolBudget;
|
|
1015
1032
|
env[TOOL_BUDGET_ZERO_AUTH_ENV] = input.allowZeroToolBudget ? "1" : undefined;
|
|
1016
|
-
|
|
1017
|
-
if (encodedChildWatchdog)
|
|
1018
|
-
env[CHILD_WATCHDOG_CONFIG_ENV] = encodedChildWatchdog;
|
|
1033
|
+
if (input.childWatchdog) env[CHILD_WATCHDOG_CONFIG_ENV] = JSON.stringify(input.childWatchdog);
|
|
1019
1034
|
|
|
1020
1035
|
env[SUBAGENT_PARENT_SESSION_ENV] =
|
|
1021
1036
|
input.parentSessionId ?? process.env[SUBAGENT_PARENT_SESSION_ENV] ?? "";
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV } from "../../shared/utils.ts";
|
|
4
5
|
|
|
5
|
-
export const
|
|
6
|
+
export const SELESAI_CODING_AGENT_PACKAGE = "@selesai/code";
|
|
6
7
|
export const SELESAI_SUBAGENT_SELESAI_BINARY_ENV = "SELESAI_SUBAGENT_SELESAI_BINARY";
|
|
7
8
|
|
|
8
9
|
export function findPiPackageRootFromEntry(
|
|
@@ -15,7 +16,7 @@ export function findPiPackageRootFromEntry(
|
|
|
15
16
|
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) as {
|
|
16
17
|
name?: unknown;
|
|
17
18
|
};
|
|
18
|
-
if (pkg.name ===
|
|
19
|
+
if (pkg.name === SELESAI_CODING_AGENT_PACKAGE) return dir;
|
|
19
20
|
}
|
|
20
21
|
dir = path.dirname(dir);
|
|
21
22
|
}
|
|
@@ -23,9 +24,13 @@ export function findPiPackageRootFromEntry(
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export function resolveInstalledPiPackageRoot(): string | undefined {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
try {
|
|
28
|
+
return findPiPackageRootFromEntry(
|
|
29
|
+
fileURLToPath(import.meta.resolve(SELESAI_CODING_AGENT_PACKAGE)),
|
|
30
|
+
);
|
|
31
|
+
} catch {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
export function resolvePiPackageRoot(): string | undefined {
|
|
@@ -58,12 +63,21 @@ interface PiSpawnCommand {
|
|
|
58
63
|
args: string[];
|
|
59
64
|
}
|
|
60
65
|
|
|
66
|
+
interface PiPackageJson {
|
|
67
|
+
name?: unknown;
|
|
68
|
+
bin?: string | Record<string, string>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function isNodeScriptPath(filePath: string): boolean {
|
|
72
|
+
return /\.(?:mjs|cjs|js)$/i.test(filePath);
|
|
73
|
+
}
|
|
74
|
+
|
|
61
75
|
function isRunnableNodeScript(
|
|
62
76
|
filePath: string,
|
|
63
77
|
existsSync: (filePath: string) => boolean,
|
|
64
78
|
): boolean {
|
|
65
79
|
if (!existsSync(filePath)) return false;
|
|
66
|
-
return
|
|
80
|
+
return isNodeScriptPath(filePath);
|
|
67
81
|
}
|
|
68
82
|
|
|
69
83
|
function normalizePath(filePath: string): string {
|
|
@@ -75,6 +89,23 @@ function isStandaloneSelesaiExecutable(execPath: string): boolean {
|
|
|
75
89
|
return /^(?:selesai|selesai-code)(?:\.exe)?$/i.test(executableName ?? "");
|
|
76
90
|
}
|
|
77
91
|
|
|
92
|
+
function resolvePiCliScriptFromPackageJson(
|
|
93
|
+
packageJsonPath: string,
|
|
94
|
+
readFileSync: (filePath: string, encoding: "utf-8") => string,
|
|
95
|
+
existsSync: (filePath: string) => boolean,
|
|
96
|
+
): string | undefined {
|
|
97
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8")) as PiPackageJson;
|
|
98
|
+
if (packageJson.name !== SELESAI_CODING_AGENT_PACKAGE) return undefined;
|
|
99
|
+
const binField = packageJson.bin;
|
|
100
|
+
const binPath =
|
|
101
|
+
typeof binField === "string"
|
|
102
|
+
? binField
|
|
103
|
+
: (binField?.selesai ?? Object.values(binField ?? {})[0]);
|
|
104
|
+
if (!binPath) return undefined;
|
|
105
|
+
const candidate = path.resolve(path.dirname(packageJsonPath), binPath);
|
|
106
|
+
return isRunnableNodeScript(candidate, existsSync) ? candidate : undefined;
|
|
107
|
+
}
|
|
108
|
+
|
|
78
109
|
export function resolvePiCliScript(
|
|
79
110
|
deps: PiSpawnDeps = {},
|
|
80
111
|
): string | undefined {
|
|
@@ -84,6 +115,7 @@ export function resolvePiCliScript(
|
|
|
84
115
|
deps.readFileSync ??
|
|
85
116
|
((filePath, encoding) => fs.readFileSync(filePath, encoding));
|
|
86
117
|
const argv1 = deps.argv1 ?? process.argv[1];
|
|
118
|
+
const env = deps.env ?? process.env;
|
|
87
119
|
|
|
88
120
|
if (argv1) {
|
|
89
121
|
const argvPath = normalizePath(argv1);
|
|
@@ -99,38 +131,29 @@ export function resolvePiCliScript(
|
|
|
99
131
|
}
|
|
100
132
|
}
|
|
101
133
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
? binField
|
|
125
|
-
: (binField?.selesai ?? Object.values(binField ?? {})[0]);
|
|
126
|
-
if (!binPath) return undefined;
|
|
127
|
-
const candidate = path.resolve(path.dirname(packageJsonPath), binPath);
|
|
128
|
-
if (isRunnableNodeScript(candidate, existsSync)) {
|
|
129
|
-
return candidate;
|
|
134
|
+
const packageJsonCandidates: Array<() => string | undefined> = [];
|
|
135
|
+
if (deps.resolvePackageJson) packageJsonCandidates.push(deps.resolvePackageJson);
|
|
136
|
+
for (const root of [deps.piPackageRoot, env[SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV], resolvePiPackageRoot()]) {
|
|
137
|
+
const trimmed = root?.trim();
|
|
138
|
+
if (trimmed) packageJsonCandidates.push(() => path.join(trimmed, "package.json"));
|
|
139
|
+
}
|
|
140
|
+
packageJsonCandidates.push(() => {
|
|
141
|
+
const packageRoot = deps.resolvePackageEntry
|
|
142
|
+
? findPiPackageRootFromEntry(deps.resolvePackageEntry())
|
|
143
|
+
: resolveInstalledPiPackageRoot();
|
|
144
|
+
return packageRoot ? path.join(packageRoot, "package.json") : undefined;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
for (const candidatePackageJson of packageJsonCandidates) {
|
|
148
|
+
try {
|
|
149
|
+
const packageJsonPath = candidatePackageJson();
|
|
150
|
+
if (!packageJsonPath) continue;
|
|
151
|
+
const candidate = resolvePiCliScriptFromPackageJson(packageJsonPath, readFileSync, existsSync);
|
|
152
|
+
if (candidate) return candidate;
|
|
153
|
+
} catch {
|
|
154
|
+
// Keep resolving; callers decide whether a PATH fallback is safe.
|
|
155
|
+
|
|
130
156
|
}
|
|
131
|
-
} catch {
|
|
132
|
-
// Verified CLI resolution is optional; falling back to `pi` lets PATH handle execution.
|
|
133
|
-
return undefined;
|
|
134
157
|
}
|
|
135
158
|
|
|
136
159
|
return undefined;
|
|
@@ -140,10 +163,18 @@ export function getPiSpawnCommand(
|
|
|
140
163
|
args: string[],
|
|
141
164
|
deps: PiSpawnDeps = {},
|
|
142
165
|
): PiSpawnCommand {
|
|
166
|
+
const platform = deps.platform ?? process.platform;
|
|
143
167
|
const env = deps.env ?? process.env;
|
|
144
168
|
const selesaiBinary = env[SELESAI_SUBAGENT_SELESAI_BINARY_ENV]?.trim();
|
|
145
169
|
if (selesaiBinary) {
|
|
170
|
+
if (platform === "win32" && isNodeScriptPath(selesaiBinary)) {
|
|
171
|
+
return {
|
|
172
|
+
command: deps.execPath ?? process.execPath,
|
|
173
|
+
args: [selesaiBinary, ...args],
|
|
174
|
+
};
|
|
175
|
+
}
|
|
146
176
|
return { command: selesaiBinary, args };
|
|
177
|
+
|
|
147
178
|
}
|
|
148
179
|
|
|
149
180
|
const execPath = deps.execPath ?? process.execPath;
|
|
@@ -158,6 +189,11 @@ export function getPiSpawnCommand(
|
|
|
158
189
|
args: [piCliPath, ...args],
|
|
159
190
|
};
|
|
160
191
|
}
|
|
192
|
+
if (platform === "win32") {
|
|
193
|
+
throw new Error(
|
|
194
|
+
`Could not resolve the Pi CLI on Windows. Set ${SELESAI_SUBAGENT_SELESAI_BINARY_ENV} or ensure ${SELESAI_CODING_AGENT_PACKAGE} is installed.`,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
161
197
|
|
|
162
198
|
return { command: "selesai", args };
|
|
163
199
|
}
|
|
@@ -5,17 +5,21 @@ import * as path from "node:path";
|
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
6
|
import { SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV } from "../../shared/utils.ts";
|
|
7
7
|
import type { JsonSchemaObject } from "../../shared/types.ts";
|
|
8
|
+
import type { ResolvedAcceptanceReportMode } from "./acceptance.ts";
|
|
8
9
|
|
|
9
10
|
export const STRUCTURED_OUTPUT_SCHEMA_ENV = "SELESAI_SUBAGENT_STRUCTURED_OUTPUT_SCHEMA";
|
|
10
11
|
export const STRUCTURED_OUTPUT_CAPTURE_ENV = "SELESAI_SUBAGENT_STRUCTURED_OUTPUT_CAPTURE";
|
|
11
12
|
export const STRUCTURED_OUTPUT_ACCEPTANCE_CAPTURE_ENV = "SELESAI_SUBAGENT_STRUCTURED_OUTPUT_ACCEPTANCE_CAPTURE";
|
|
13
|
+
export const STRUCTURED_OUTPUT_ACCEPTANCE_REQUIRED_ENV = "SELESAI_SUBAGENT_STRUCTURED_OUTPUT_ACCEPTANCE_REQUIRED";
|
|
12
14
|
export const MISSING_STRUCTURED_OUTPUT_CALL_ERROR = "Missing structured_output call; this step has outputSchema and must finish by calling structured_output.";
|
|
15
|
+
export const MISSING_STRUCTURED_ACCEPTANCE_REPORT_ERROR = "Missing acceptanceReport in structured_output call; acceptance.report is \"on\".";
|
|
13
16
|
|
|
14
17
|
export interface StructuredOutputRuntime {
|
|
15
18
|
schema: JsonSchemaObject;
|
|
16
19
|
schemaPath: string;
|
|
17
20
|
outputPath: string;
|
|
18
21
|
acceptanceReportPath?: string;
|
|
22
|
+
acceptanceReportRequired?: boolean;
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
const SCHEMA_MAP_KEYWORDS = ["properties", "patternProperties", "$defs", "definitions", "dependentSchemas"] as const;
|
|
@@ -61,14 +65,14 @@ function rewriteLocalJsonPointerRefs(schema: unknown, pointerPrefix: string, inh
|
|
|
61
65
|
return rewritten;
|
|
62
66
|
}
|
|
63
67
|
|
|
64
|
-
export function createStructuredOutputToolParameters(schema: JsonSchemaObject, options: { acceptanceReport?:
|
|
68
|
+
export function createStructuredOutputToolParameters(schema: JsonSchemaObject, options: { acceptanceReport?: "optional" | "required" } = {}): JsonSchemaObject {
|
|
65
69
|
return {
|
|
66
70
|
type: "object",
|
|
67
71
|
properties: {
|
|
68
72
|
value: rewriteLocalJsonPointerRefs(schema, "#/properties/value"),
|
|
69
73
|
...(options.acceptanceReport ? { acceptanceReport: { type: "object" } } : {}),
|
|
70
74
|
},
|
|
71
|
-
required: ["value"],
|
|
75
|
+
required: ["value", ...(options.acceptanceReport === "required" ? ["acceptanceReport"] : [])],
|
|
72
76
|
additionalProperties: false,
|
|
73
77
|
};
|
|
74
78
|
}
|
|
@@ -129,7 +133,7 @@ export function assertJsonSchemaObject(schema: unknown, label = "outputSchema"):
|
|
|
129
133
|
}
|
|
130
134
|
}
|
|
131
135
|
|
|
132
|
-
export function createStructuredOutputRuntime(schema: JsonSchemaObject, baseDir?: string, options: {
|
|
136
|
+
export function createStructuredOutputRuntime(schema: JsonSchemaObject, baseDir?: string, options: { acceptanceReport?: ResolvedAcceptanceReportMode } = {}): StructuredOutputRuntime {
|
|
133
137
|
assertJsonSchemaObject(schema);
|
|
134
138
|
const rootDir = baseDir ?? os.tmpdir();
|
|
135
139
|
fs.mkdirSync(rootDir, { recursive: true });
|
|
@@ -137,7 +141,14 @@ export function createStructuredOutputRuntime(schema: JsonSchemaObject, baseDir?
|
|
|
137
141
|
const schemaPath = path.join(dir, "schema.json");
|
|
138
142
|
const outputPath = path.join(dir, "output.json");
|
|
139
143
|
fs.writeFileSync(schemaPath, JSON.stringify(schema), { mode: 0o600 });
|
|
140
|
-
return {
|
|
144
|
+
return {
|
|
145
|
+
schema,
|
|
146
|
+
schemaPath,
|
|
147
|
+
outputPath,
|
|
148
|
+
...(options.acceptanceReport && options.acceptanceReport !== "off"
|
|
149
|
+
? { acceptanceReportPath: path.join(dir, "acceptance-report.json"), acceptanceReportRequired: options.acceptanceReport === "required" }
|
|
150
|
+
: {}),
|
|
151
|
+
};
|
|
141
152
|
}
|
|
142
153
|
|
|
143
154
|
export async function validateStructuredOutputValue(schema: JsonSchemaObject, value: unknown): Promise<{ status: "valid" } | { status: "invalid"; message: string }> {
|
|
@@ -178,14 +189,30 @@ export async function readStructuredOutput(runtime: StructuredOutputRuntime): Pr
|
|
|
178
189
|
}
|
|
179
190
|
|
|
180
191
|
export function readStructuredOutputAcceptanceReport(runtime: StructuredOutputRuntime): { value?: unknown; error?: string } {
|
|
181
|
-
if (!runtime.acceptanceReportPath
|
|
192
|
+
if (!runtime.acceptanceReportPath) return {};
|
|
193
|
+
if (!fs.existsSync(runtime.acceptanceReportPath)) {
|
|
194
|
+
return runtime.acceptanceReportRequired ? { error: MISSING_STRUCTURED_ACCEPTANCE_REPORT_ERROR } : {};
|
|
195
|
+
}
|
|
182
196
|
try {
|
|
183
|
-
return { value: JSON.parse(fs.readFileSync(runtime.acceptanceReportPath, "utf-8")) };
|
|
197
|
+
return { value: JSON.parse(fs.readFileSync(runtime.acceptanceReportPath, "utf-8")) as unknown };
|
|
184
198
|
} catch (error) {
|
|
185
199
|
return { error: `Failed to read structured output acceptance report: ${error instanceof Error ? error.message : String(error)}` };
|
|
186
200
|
}
|
|
187
201
|
}
|
|
188
202
|
|
|
203
|
+
export function clearStructuredOutputCaptures(runtime: StructuredOutputRuntime): string | undefined {
|
|
204
|
+
let cleanupError: string | undefined;
|
|
205
|
+
for (const filePath of [runtime.outputPath, runtime.acceptanceReportPath]) {
|
|
206
|
+
if (!filePath) continue;
|
|
207
|
+
try {
|
|
208
|
+
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
cleanupError ??= `Failed to clear stale structured output capture ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return cleanupError;
|
|
214
|
+
}
|
|
215
|
+
|
|
189
216
|
export function cleanupStructuredOutputRuntime(runtime: StructuredOutputRuntime | undefined): void {
|
|
190
217
|
if (!runtime) return;
|
|
191
218
|
try {
|
|
@@ -45,11 +45,12 @@ export function resolveControlConfig(
|
|
|
45
45
|
const enabled = override?.enabled ?? globalConfig?.enabled ?? DEFAULT_CONTROL_CONFIG.enabled;
|
|
46
46
|
const overrideNeedsAttentionAfterMs = parsePositiveInt(override?.needsAttentionAfterMs);
|
|
47
47
|
const globalNeedsAttentionAfterMs = parsePositiveInt(globalConfig?.needsAttentionAfterMs);
|
|
48
|
+
const globalNeedsAttentionAfterMsIsExplicit = (globalConfig as Partial<ResolvedControlConfig> | undefined)?.needsAttentionAfterMsIsExplicit ?? globalNeedsAttentionAfterMs !== undefined;
|
|
48
49
|
const needsAttentionAfterMs = overrideNeedsAttentionAfterMs
|
|
49
50
|
?? globalNeedsAttentionAfterMs
|
|
50
51
|
?? DEFAULT_CONTROL_CONFIG.needsAttentionAfterMs;
|
|
51
52
|
const needsAttentionAfterMsIsExplicit = overrideNeedsAttentionAfterMs !== undefined
|
|
52
|
-
||
|
|
53
|
+
|| globalNeedsAttentionAfterMsIsExplicit;
|
|
53
54
|
const activeNoticeAfterMs = parsePositiveInt(override?.activeNoticeAfterMs)
|
|
54
55
|
?? parsePositiveInt(globalConfig?.activeNoticeAfterMs)
|
|
55
56
|
?? DEFAULT_CONTROL_CONFIG.activeNoticeAfterMs;
|
|
@@ -98,11 +99,12 @@ export function deriveActivityState(input: {
|
|
|
98
99
|
config: ResolvedControlConfig;
|
|
99
100
|
startedAt: number;
|
|
100
101
|
lastActivityAt?: number;
|
|
102
|
+
turnCount?: number;
|
|
101
103
|
currentTool?: string;
|
|
102
104
|
thinking?: string | false;
|
|
103
105
|
now?: number;
|
|
104
106
|
}): ActivityState | undefined {
|
|
105
|
-
if (!input.config.enabled || input.currentTool) return undefined;
|
|
107
|
+
if (!input.config.enabled || input.currentTool || (input.turnCount ?? 0) === 0) return undefined;
|
|
106
108
|
const now = input.now ?? Date.now();
|
|
107
109
|
const lastActivity = input.lastActivityAt ?? input.startedAt;
|
|
108
110
|
const ageMs = Math.max(0, now - lastActivity);
|
|
@@ -8,7 +8,8 @@ import { decodePermissionRules, permissionDecision, PERMISSION_AUDIT_PATH_ENV, P
|
|
|
8
8
|
import { consumeSteerRequestsFromDir, MAX_STEER_QUEUE_SIZE, steerAckPathFromDir, writeSteerAckAt, writeSteerCapabilityAt, writeSteerRequestToDir, type SteerDeliveryStatus, type SteerRequest } from "../background/control-channel.ts";
|
|
9
9
|
import { SUBAGENT_CHILD_AGENT_ENV, SUBAGENT_CHILD_INDEX_ENV, SUBAGENT_FANOUT_CHILD_ENV, SUBAGENT_FORK_CACHE_KEY_ENV, SUBAGENT_INHERIT_GLOBAL_CONTEXT_ENV, SUBAGENT_STEER_ACK_DIR_ENV, SUBAGENT_STEER_CAPABILITY_ENV, SUBAGENT_STEER_INBOX_ENV } from "./pi-args.ts";
|
|
10
10
|
import { RUNTIME_EXTENSION_ACK_EVENT, RUNTIME_EXTENSION_ACK_PATH_ENV, isRuntimeAcknowledgedExtensionId, writeRuntimeAcknowledgedExtensions } from "./runtime-acknowledged-extensions.ts";
|
|
11
|
-
import { createStructuredOutputToolParameters, STRUCTURED_OUTPUT_ACCEPTANCE_CAPTURE_ENV, STRUCTURED_OUTPUT_CAPTURE_ENV, STRUCTURED_OUTPUT_SCHEMA_ENV, validateStructuredOutputValue } from "./structured-output.ts";
|
|
11
|
+
import { createStructuredOutputToolParameters, MISSING_STRUCTURED_ACCEPTANCE_REPORT_ERROR, STRUCTURED_OUTPUT_ACCEPTANCE_CAPTURE_ENV, STRUCTURED_OUTPUT_ACCEPTANCE_REQUIRED_ENV, STRUCTURED_OUTPUT_CAPTURE_ENV, STRUCTURED_OUTPUT_SCHEMA_ENV, validateStructuredOutputValue } from "./structured-output.ts";
|
|
12
|
+
import { validateAcceptanceReport } from "./acceptance.ts";
|
|
12
13
|
import {
|
|
13
14
|
CHILD_TOOL_DIAGNOSTIC_PATH_ENV,
|
|
14
15
|
formatChildToolDiagnostic,
|
|
@@ -730,10 +731,14 @@ export default function registerSubagentPromptRuntime(pi: ExtensionAPI): void {
|
|
|
730
731
|
});
|
|
731
732
|
const structuredOutputPath = process.env[STRUCTURED_OUTPUT_CAPTURE_ENV];
|
|
732
733
|
const structuredAcceptanceReportPath = process.env[STRUCTURED_OUTPUT_ACCEPTANCE_CAPTURE_ENV];
|
|
734
|
+
const structuredAcceptanceReportRequired = process.env[STRUCTURED_OUTPUT_ACCEPTANCE_REQUIRED_ENV] === "1";
|
|
733
735
|
const structuredSchemaPath = process.env[STRUCTURED_OUTPUT_SCHEMA_ENV];
|
|
734
736
|
if (structuredOutputPath && structuredSchemaPath) {
|
|
735
737
|
const schema = JSON.parse(fs.readFileSync(structuredSchemaPath, "utf-8")) as JsonSchemaObject;
|
|
736
|
-
const
|
|
738
|
+
const acceptanceReportMode = structuredAcceptanceReportPath
|
|
739
|
+
? structuredAcceptanceReportRequired ? "required" as const : "optional" as const
|
|
740
|
+
: undefined;
|
|
741
|
+
const parameters = createStructuredOutputToolParameters(schema, { acceptanceReport: acceptanceReportMode });
|
|
737
742
|
const registerTool = pi.registerTool as unknown as (tool: {
|
|
738
743
|
name: string;
|
|
739
744
|
label: string;
|
|
@@ -751,11 +756,23 @@ export default function registerSubagentPromptRuntime(pi: ExtensionAPI): void {
|
|
|
751
756
|
if (validation.status === "invalid") {
|
|
752
757
|
throw new Error(`Structured output validation failed: ${validation.message}`);
|
|
753
758
|
}
|
|
759
|
+
if (structuredAcceptanceReportRequired && params.acceptanceReport === undefined) {
|
|
760
|
+
throw new Error(MISSING_STRUCTURED_ACCEPTANCE_REPORT_ERROR);
|
|
761
|
+
}
|
|
762
|
+
if (structuredAcceptanceReportRequired && params.acceptanceReport !== undefined) {
|
|
763
|
+
const acceptanceValidation = validateAcceptanceReport(params.acceptanceReport, "acceptanceReport");
|
|
764
|
+
if (!acceptanceValidation.report) {
|
|
765
|
+
throw new Error(`Invalid structured output acceptance report: ${acceptanceValidation.errors.join("; ")}`);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
754
768
|
fs.mkdirSync(path.dirname(structuredOutputPath), { recursive: true });
|
|
755
|
-
fs.writeFileSync(structuredOutputPath, JSON.stringify(params.value), { mode: 0o600 });
|
|
756
769
|
if (structuredAcceptanceReportPath && params.acceptanceReport !== undefined) {
|
|
770
|
+
fs.mkdirSync(path.dirname(structuredAcceptanceReportPath), { recursive: true });
|
|
757
771
|
fs.writeFileSync(structuredAcceptanceReportPath, JSON.stringify(params.acceptanceReport), { mode: 0o600 });
|
|
772
|
+
} else if (structuredAcceptanceReportPath && fs.existsSync(structuredAcceptanceReportPath)) {
|
|
773
|
+
fs.unlinkSync(structuredAcceptanceReportPath);
|
|
758
774
|
}
|
|
775
|
+
fs.writeFileSync(structuredOutputPath, JSON.stringify(params.value), { mode: 0o600 });
|
|
759
776
|
return {
|
|
760
777
|
content: [{ type: "text", text: "Structured output captured." }],
|
|
761
778
|
details: { path: structuredOutputPath },
|