@runfusion/fusion 0.30.0 → 0.31.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.
- package/dist/bin.js +438 -235
- package/dist/client/assets/{AgentDetailView-Cu4TgvBn.js → AgentDetailView-Dx7T1-gM.js} +3 -3
- package/dist/client/assets/AgentPermissionPolicyEditor-Cbd6SG_T.css +1 -0
- package/dist/client/assets/AgentPermissionPolicyEditor-Ia7QkynK.js +6 -0
- package/dist/client/assets/{AgentsView-CIH4Acck.js → AgentsView-DBbdPWV-.js} +3 -3
- package/dist/client/assets/ChatView-DYpoWnCc.js +1 -0
- package/dist/client/assets/{DevServerView-Bd8JZ-3C.js → DevServerView-DotHOFZt.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-BOGqZNiq.js → DirectoryPicker-CIj3ZGKv.js} +1 -1
- package/dist/client/assets/{DocumentsView-B-ELcw4o.js → DocumentsView-Dt27gDYa.js} +1 -1
- package/dist/client/assets/{EvalsView-Ccl9QBYN.js → EvalsView-HfWTc8Uv.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-BM7EnSvJ.js → ExperimentalAgentOnboardingModal-U7fXDhhN.js} +1 -1
- package/dist/client/assets/{InsightsView-DLF49bzu.js → InsightsView-C8bsAlMv.js} +1 -1
- package/dist/client/assets/{MemoryView-CE1D9euM.js → MemoryView-D-HLtRrr.js} +1 -1
- package/dist/client/assets/{NodesView-Zxusnhxu.js → NodesView-w3ms-XX2.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-BlMmxtLZ.js → PiExtensionsManager-DFAZAfNn.js} +2 -2
- package/dist/client/assets/{PluginManager-D4eZ0zA4.js → PluginManager-DN1VG7Qm.js} +1 -1
- package/dist/client/assets/ReliabilityView-D0Rtd7pX.css +1 -0
- package/dist/client/assets/{ResearchView-C14Zh7c9.js → ResearchView-YoxEFtNB.js} +1 -1
- package/dist/client/assets/{SettingsModal-BAx4gt2A.css → SettingsModal-1Y8N6MkL.css} +1 -1
- package/dist/client/assets/SettingsModal-B86XhYB1.js +31 -0
- package/dist/client/assets/{SettingsModal-DRnZ6eBW.js → SettingsModal-BRRPoxku.js} +1 -1
- package/dist/client/assets/{SetupWizardModal-BmmRM3g-.js → SetupWizardModal-B4eA3bpz.js} +1 -1
- package/dist/client/assets/{SkillsView-DWVtT2-8.js → SkillsView-DZIfZoF5.js} +1 -1
- package/dist/client/assets/{StashRecoveryView-CKkaWzzc.js → StashRecoveryView-mKXYsqam.js} +1 -1
- package/dist/client/assets/{TodoView-hbD0NOCY.js → TodoView-C6sLLEQu.js} +1 -1
- package/dist/client/assets/dashboard-view-DOY8-BIx.js +21 -0
- package/dist/client/assets/{folder-open-BTN2_Wuv.js → folder-open-C_fxRMq1.js} +1 -1
- package/dist/client/assets/index-DTFPgu6_.js +695 -0
- package/dist/client/assets/index-DdvAZIzj.css +1 -0
- package/dist/client/assets/{upload-euJEs_N2.js → upload-xjku_93-.js} +1 -1
- package/dist/client/assets/{users-B7miEHCZ.js → users-CbALDtDk.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/package.json +1 -1
- package/dist/extension.js +477 -225
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
- package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
- package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-reports/package.json +1 -1
- package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
- package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
- package/package.json +1 -1
- package/skill/fusion/SKILL.md +1 -1
- package/skill/fusion/references/extension-tools.md +11 -0
- package/skill/fusion/references/fusion-capabilities.md +1 -0
- package/dist/client/assets/AgentPermissionPolicyEditor-Ceg2Dszn.js +0 -6
- package/dist/client/assets/AgentPermissionPolicyEditor-CthjqUL_.css +0 -1
- package/dist/client/assets/ChatView-DqJoFquZ.js +0 -1
- package/dist/client/assets/ReliabilityView-rxA_HgEG.css +0 -1
- package/dist/client/assets/SettingsModal-c1QwWno0.js +0 -31
- package/dist/client/assets/dashboard-view-VNPB3ca0.js +0 -21
- package/dist/client/assets/index-CZFBq256.css +0 -1
- package/dist/client/assets/index-DzSLVOnp.js +0 -695
- /package/dist/client/assets/{ReliabilityView-KmH60I_5.js → ReliabilityView-DAuqE-zi.js} +0 -0
package/dist/bin.js
CHANGED
|
@@ -866,6 +866,7 @@ Your job: help users transform high-level goals into structured mission plans wi
|
|
|
866
866
|
- Features should be specific and actionable
|
|
867
867
|
- ALWAYS include verification/acceptance criteria at every level:
|
|
868
868
|
- Milestone: "verification" field \u2014 how to confirm this phase is complete (e.g., "All API endpoints return correct responses, integration tests pass")
|
|
869
|
+
- Milestone: optional "acceptanceCriteria" field for explicit milestone-level completion bars (if omitted, acceptance can be auto-derived from child feature criteria/descriptions)
|
|
869
870
|
- Slice: "verification" field \u2014 how to confirm this work unit is done (e.g., "Auth flow works end-to-end from signup through login")
|
|
870
871
|
- Feature: "acceptanceCriteria" field \u2014 how to verify this specific deliverable (e.g., "JWT tokens expire after 1 hour and refresh correctly")
|
|
871
872
|
- Suggest sensible defaults and push for specificity
|
|
@@ -879,7 +880,7 @@ For questions:
|
|
|
879
880
|
{"type": "question", "data": {"id": "unique-id", "type": "text|single_select|multi_select|confirm", "question": "The question text", "description": "Helpful context", "options": [{"id": "opt1", "label": "Option 1", "description": "Details"}]}}
|
|
880
881
|
|
|
881
882
|
For completion (when you have enough information):
|
|
882
|
-
{"type": "complete", "data": {"missionTitle": "Refined mission title", "missionDescription": "Comprehensive mission description based on the conversation", "milestones": [{"title": "Milestone title", "description": "What this phase achieves", "verification": "How to confirm this milestone is complete", "slices": [{"title": "Slice title", "description": "What this work unit covers", "verification": "How to confirm this slice is done", "features": [{"title": "Feature title", "description": "What to build", "acceptanceCriteria": "How to verify this feature works"}]}]}]}}`
|
|
883
|
+
{"type": "complete", "data": {"missionTitle": "Refined mission title", "missionDescription": "Comprehensive mission description based on the conversation", "milestones": [{"title": "Milestone title", "description": "What this phase achieves", "verification": "How to confirm this milestone is complete", "acceptanceCriteria": "Optional explicit milestone criteria (omit to auto-derive from features)", "slices": [{"title": "Slice title", "description": "What this work unit covers", "verification": "How to confirm this slice is done", "features": [{"title": "Feature title", "description": "What to build", "acceptanceCriteria": "How to verify this feature works"}]}]}]}}`
|
|
883
884
|
},
|
|
884
885
|
"ai-refine-system": {
|
|
885
886
|
key: "ai-refine-system",
|
|
@@ -1451,7 +1452,7 @@ function validateMessageMetadata(metadata) {
|
|
|
1451
1452
|
throw new Error("metadata.wakeRecipient must be a boolean");
|
|
1452
1453
|
}
|
|
1453
1454
|
}
|
|
1454
|
-
var THINKING_LEVELS, COLUMNS, DEFAULT_COLUMN, TASK_PRIORITIES, DEFAULT_TASK_PRIORITY, HIGH_FANOUT_BLOCKER_TODO_THRESHOLD, STALE_HIGH_FANOUT_BLOCKER_AGE_THRESHOLD_MS, EXECUTION_MODES, DEFAULT_EXECUTION_MODE, THEME_MODES, COLOR_THEMES, MERGE_STRATEGY_OVERLAP_BEHAVIORS, POST_MERGE_AUDIT_MODES, MERGE_AUDIT_AUTO_RECOVERY_MODES, AUTO_RECOVERY_MODES, NOTIFICATION_EVENTS, WORKFLOW_STEP_TEMPLATES, DOCUMENT_KEY_RE, REPO_OVERRIDE_RE, CheckoutConflictError, SHARED_STATE_SNAPSHOT_VERSION, COLUMN_LABELS, COLUMN_DESCRIPTIONS, VALID_TRANSITIONS, AGENT_VALID_TRANSITIONS, AGENT_PERMISSIONS, PERMANENT_AGENT_ACTION_CATEGORIES, AGENT_PERMISSION_POLICY_ACTION_CATEGORIES, AGENT_PROVISIONING_APPROVAL_MODES, AGENT_PERMISSION_POLICY_PRESET_IDS, APPROVAL_REQUEST_STATUSES, APPROVAL_REQUEST_AUDIT_EVENT_TYPES, LEGACY_AGENT_PERMISSION_POLICY_ACTION_CATEGORY_ALIASES, DEFAULT_HEARTBEAT_PROCEDURE_PATH, DASHBOARD_USER_ID, DASHBOARD_USER_ALIASES;
|
|
1455
|
+
var THINKING_LEVELS, COLUMNS, DEFAULT_COLUMN, TASK_PRIORITIES, DEFAULT_TASK_PRIORITY, HIGH_FANOUT_BLOCKER_TODO_THRESHOLD, STALE_HIGH_FANOUT_BLOCKER_AGE_THRESHOLD_MS, EXECUTION_MODES, DEFAULT_EXECUTION_MODE, THEME_MODES, COLOR_THEMES, MERGE_STRATEGY_OVERLAP_BEHAVIORS, POST_MERGE_AUDIT_MODES, MERGE_AUDIT_AUTO_RECOVERY_MODES, AUTO_RECOVERY_MODES, NOTIFICATION_EVENTS, WORKFLOW_STEP_TEMPLATES, DOCUMENT_KEY_RE, REPO_OVERRIDE_RE, CheckoutConflictError, SHARED_STATE_SNAPSHOT_VERSION, COLUMN_LABELS, COLUMN_DESCRIPTIONS, VALID_TRANSITIONS, AGENT_VALID_TRANSITIONS, AGENT_PERMISSIONS, PERMANENT_AGENT_ACTION_CATEGORIES, AGENT_PERMISSION_POLICY_ACTION_CATEGORIES, AGENT_PERMISSION_POLICY_CATEGORY_TOOL_EXAMPLES, AGENT_PERMISSION_POLICY_EXEMPT_TOOL_EXAMPLES, AGENT_PROVISIONING_APPROVAL_MODES, AGENT_PERMISSION_POLICY_PRESET_IDS, APPROVAL_REQUEST_STATUSES, APPROVAL_REQUEST_AUDIT_EVENT_TYPES, LEGACY_AGENT_PERMISSION_POLICY_ACTION_CATEGORY_ALIASES, DEFAULT_HEARTBEAT_PROCEDURE_PATH, DASHBOARD_USER_ID, DASHBOARD_USER_ALIASES;
|
|
1455
1456
|
var init_types = __esm({
|
|
1456
1457
|
"../core/src/types.ts"() {
|
|
1457
1458
|
"use strict";
|
|
@@ -1885,6 +1886,36 @@ Do NOT spend time on nits when no real issues exist.`
|
|
|
1885
1886
|
"network_api",
|
|
1886
1887
|
"task_agent_mutation"
|
|
1887
1888
|
];
|
|
1889
|
+
AGENT_PERMISSION_POLICY_CATEGORY_TOOL_EXAMPLES = {
|
|
1890
|
+
git_write: ["git commit", "git push", "git merge", "git branch -d", "git worktree add", "write", "edit"],
|
|
1891
|
+
file_write_delete: ["write", "edit", "fn_task_attach"],
|
|
1892
|
+
command_execution: ["bash (non-git)", "read", "find", "grep", "ls"],
|
|
1893
|
+
network_api: ["fn_research_run (web/research)", "fn_web_fetch"],
|
|
1894
|
+
task_agent_mutation: [
|
|
1895
|
+
"fn_task_create",
|
|
1896
|
+
"fn_delegate_task",
|
|
1897
|
+
"fn_task_import_github",
|
|
1898
|
+
"fn_task_import_github_issue",
|
|
1899
|
+
"fn_spawn_agent",
|
|
1900
|
+
"fn_update_agent_config",
|
|
1901
|
+
"fn_update_identity"
|
|
1902
|
+
]
|
|
1903
|
+
};
|
|
1904
|
+
AGENT_PERMISSION_POLICY_EXEMPT_TOOL_EXAMPLES = [
|
|
1905
|
+
"fn_send_message",
|
|
1906
|
+
"fn_post_room_message",
|
|
1907
|
+
"fn_read_messages",
|
|
1908
|
+
"fn_task_log",
|
|
1909
|
+
"fn_task_done",
|
|
1910
|
+
"fn_heartbeat_done",
|
|
1911
|
+
"fn_task_document_write",
|
|
1912
|
+
"fn_task_document_read",
|
|
1913
|
+
"fn_memory_search",
|
|
1914
|
+
"fn_memory_get",
|
|
1915
|
+
"fn_memory_append",
|
|
1916
|
+
"fn_read_evaluations",
|
|
1917
|
+
"fn_reflect_on_performance"
|
|
1918
|
+
];
|
|
1888
1919
|
AGENT_PROVISIONING_APPROVAL_MODES = ["always", "trusted-only", "never"];
|
|
1889
1920
|
AGENT_PERMISSION_POLICY_PRESET_IDS = ["unrestricted", "approval-required", "locked-down", "custom"];
|
|
1890
1921
|
APPROVAL_REQUEST_STATUSES = ["pending", "approved", "denied", "completed"];
|
|
@@ -3727,7 +3758,7 @@ var init_db = __esm({
|
|
|
3727
3758
|
DEFAULT_SQLITE_BUSY_TIMEOUT_MS = 5e3;
|
|
3728
3759
|
DEFAULT_SQLITE_LOCK_RECOVERY_WINDOW_MS = 1e3;
|
|
3729
3760
|
DEFAULT_SQLITE_LOCK_RECOVERY_DELAY_MS = 50;
|
|
3730
|
-
SCHEMA_VERSION =
|
|
3761
|
+
SCHEMA_VERSION = 81;
|
|
3731
3762
|
SCHEMA_SQL = `
|
|
3732
3763
|
-- Tasks table with JSON columns for nested data
|
|
3733
3764
|
CREATE TABLE IF NOT EXISTS tasks (
|
|
@@ -3782,6 +3813,8 @@ CREATE TABLE IF NOT EXISTS tasks (
|
|
|
3782
3813
|
createdAt TEXT NOT NULL,
|
|
3783
3814
|
updatedAt TEXT NOT NULL,
|
|
3784
3815
|
columnMovedAt TEXT,
|
|
3816
|
+
firstExecutionAt TEXT,
|
|
3817
|
+
cumulativeActiveMs INTEGER,
|
|
3785
3818
|
executionStartedAt TEXT,
|
|
3786
3819
|
executionCompletedAt TEXT,
|
|
3787
3820
|
-- JSON columns for nested arrays/objects
|
|
@@ -4249,6 +4282,7 @@ CREATE TABLE IF NOT EXISTS milestones (
|
|
|
4249
4282
|
orderIndex INTEGER NOT NULL,
|
|
4250
4283
|
interviewState TEXT NOT NULL,
|
|
4251
4284
|
dependencies TEXT DEFAULT '[]',
|
|
4285
|
+
acceptanceCriteria TEXT,
|
|
4252
4286
|
createdAt TEXT NOT NULL,
|
|
4253
4287
|
updatedAt TEXT NOT NULL,
|
|
4254
4288
|
FOREIGN KEY (missionId) REFERENCES missions(id) ON DELETE CASCADE
|
|
@@ -6369,6 +6403,25 @@ This means a caller passed a .fusion directory where a project root was expected
|
|
|
6369
6403
|
this.addColumnIfMissing("tasks", "overlapBlockedBy", "TEXT");
|
|
6370
6404
|
});
|
|
6371
6405
|
}
|
|
6406
|
+
if (version < 80) {
|
|
6407
|
+
this.applyMigration(80, () => {
|
|
6408
|
+
this.addColumnIfMissing("milestones", "acceptanceCriteria", "TEXT");
|
|
6409
|
+
});
|
|
6410
|
+
}
|
|
6411
|
+
if (version < 81) {
|
|
6412
|
+
this.applyMigration(81, () => {
|
|
6413
|
+
this.addColumnIfMissing("tasks", "firstExecutionAt", "TEXT");
|
|
6414
|
+
this.addColumnIfMissing("tasks", "cumulativeActiveMs", "INTEGER");
|
|
6415
|
+
if (this.hasColumn("tasks", "executionStartedAt")) {
|
|
6416
|
+
this.db.prepare(
|
|
6417
|
+
`UPDATE tasks
|
|
6418
|
+
SET firstExecutionAt = executionStartedAt
|
|
6419
|
+
WHERE firstExecutionAt IS NULL
|
|
6420
|
+
AND executionStartedAt IS NOT NULL`
|
|
6421
|
+
).run();
|
|
6422
|
+
}
|
|
6423
|
+
});
|
|
6424
|
+
}
|
|
6372
6425
|
}
|
|
6373
6426
|
/**
|
|
6374
6427
|
* Run a single migration step inside a transaction and bump the version.
|
|
@@ -10611,6 +10664,21 @@ var init_db_migrate = __esm({
|
|
|
10611
10664
|
|
|
10612
10665
|
// ../core/src/mission-store.ts
|
|
10613
10666
|
import { EventEmitter as EventEmitter4 } from "node:events";
|
|
10667
|
+
function deriveMilestoneAcceptanceCriteriaFromFeatures(features) {
|
|
10668
|
+
const lines = features.map((feature) => {
|
|
10669
|
+
const acceptance = feature.acceptanceCriteria?.trim();
|
|
10670
|
+
const description = feature.description?.trim();
|
|
10671
|
+
const text = acceptance && acceptance.length > 0 ? acceptance : description && description.length > 0 ? description : void 0;
|
|
10672
|
+
if (!text) {
|
|
10673
|
+
return void 0;
|
|
10674
|
+
}
|
|
10675
|
+
return `- ${feature.title}: ${text}`;
|
|
10676
|
+
}).filter((line) => Boolean(line));
|
|
10677
|
+
if (lines.length === 0) {
|
|
10678
|
+
return void 0;
|
|
10679
|
+
}
|
|
10680
|
+
return lines.join("\n");
|
|
10681
|
+
}
|
|
10614
10682
|
var DEFAULT_IMPLEMENTATION_RETRY_BUDGET, MissionStore;
|
|
10615
10683
|
var init_mission_store = __esm({
|
|
10616
10684
|
"../core/src/mission-store.ts"() {
|
|
@@ -10672,6 +10740,7 @@ var init_mission_store = __esm({
|
|
|
10672
10740
|
dependencies: fromJson(row.dependencies) || [],
|
|
10673
10741
|
planningNotes: row.planningNotes || void 0,
|
|
10674
10742
|
verification: row.verification || void 0,
|
|
10743
|
+
acceptanceCriteria: row.acceptanceCriteria || void 0,
|
|
10675
10744
|
validationState: row.validationState || "not_started",
|
|
10676
10745
|
createdAt: row.createdAt,
|
|
10677
10746
|
updatedAt: row.updatedAt
|
|
@@ -11384,13 +11453,14 @@ var init_mission_store = __esm({
|
|
|
11384
11453
|
dependencies: input.dependencies || [],
|
|
11385
11454
|
planningNotes: input.planningNotes,
|
|
11386
11455
|
verification: input.verification,
|
|
11456
|
+
acceptanceCriteria: input.acceptanceCriteria,
|
|
11387
11457
|
validationState: "not_started",
|
|
11388
11458
|
createdAt: now,
|
|
11389
11459
|
updatedAt: now
|
|
11390
11460
|
};
|
|
11391
11461
|
this.db.prepare(`
|
|
11392
|
-
INSERT INTO milestones (id, missionId, title, description, status, orderIndex, interviewState, dependencies, planningNotes, verification, validationState, createdAt, updatedAt)
|
|
11393
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
11462
|
+
INSERT INTO milestones (id, missionId, title, description, status, orderIndex, interviewState, dependencies, planningNotes, verification, acceptanceCriteria, validationState, createdAt, updatedAt)
|
|
11463
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
11394
11464
|
`).run(
|
|
11395
11465
|
milestone.id,
|
|
11396
11466
|
milestone.missionId,
|
|
@@ -11402,6 +11472,7 @@ var init_mission_store = __esm({
|
|
|
11402
11472
|
toJson(milestone.dependencies),
|
|
11403
11473
|
milestone.planningNotes ?? null,
|
|
11404
11474
|
milestone.verification ?? null,
|
|
11475
|
+
milestone.acceptanceCriteria ?? null,
|
|
11405
11476
|
milestone.validationState,
|
|
11406
11477
|
milestone.createdAt,
|
|
11407
11478
|
milestone.updatedAt
|
|
@@ -11465,6 +11536,7 @@ var init_mission_store = __esm({
|
|
|
11465
11536
|
dependencies = ?,
|
|
11466
11537
|
planningNotes = ?,
|
|
11467
11538
|
verification = ?,
|
|
11539
|
+
acceptanceCriteria = ?,
|
|
11468
11540
|
validationState = ?,
|
|
11469
11541
|
updatedAt = ?
|
|
11470
11542
|
WHERE id = ?
|
|
@@ -11477,6 +11549,7 @@ var init_mission_store = __esm({
|
|
|
11477
11549
|
toJson(updated.dependencies),
|
|
11478
11550
|
updated.planningNotes ?? null,
|
|
11479
11551
|
updated.verification ?? null,
|
|
11552
|
+
updated.acceptanceCriteria ?? null,
|
|
11480
11553
|
updated.validationState || "not_started",
|
|
11481
11554
|
updated.updatedAt,
|
|
11482
11555
|
updated.id
|
|
@@ -11540,6 +11613,21 @@ var init_mission_store = __esm({
|
|
|
11540
11613
|
updateMilestoneInterviewState(id, state) {
|
|
11541
11614
|
return this.updateMilestone(id, { interviewState: state });
|
|
11542
11615
|
}
|
|
11616
|
+
applyDerivedMilestoneAcceptanceCriteria(milestoneId) {
|
|
11617
|
+
const milestone = this.getMilestone(milestoneId);
|
|
11618
|
+
if (!milestone) {
|
|
11619
|
+
throw new Error(`Milestone ${milestoneId} not found`);
|
|
11620
|
+
}
|
|
11621
|
+
if (milestone.acceptanceCriteria?.trim()) {
|
|
11622
|
+
return milestone;
|
|
11623
|
+
}
|
|
11624
|
+
const features = this.listSlices(milestoneId).flatMap((slice) => this.listFeatures(slice.id));
|
|
11625
|
+
const derivedAcceptanceCriteria = deriveMilestoneAcceptanceCriteriaFromFeatures(features);
|
|
11626
|
+
if (!derivedAcceptanceCriteria) {
|
|
11627
|
+
return milestone;
|
|
11628
|
+
}
|
|
11629
|
+
return this.updateMilestone(milestoneId, { acceptanceCriteria: derivedAcceptanceCriteria });
|
|
11630
|
+
}
|
|
11543
11631
|
// ── Slice Operations ───────────────────────────────────────────────
|
|
11544
11632
|
/**
|
|
11545
11633
|
* Add a slice to a milestone.
|
|
@@ -11811,6 +11899,7 @@ var init_mission_store = __esm({
|
|
|
11811
11899
|
this.db.bumpLastModified();
|
|
11812
11900
|
this.emit("feature:created", feature);
|
|
11813
11901
|
this.recomputeSliceStatus(sliceId);
|
|
11902
|
+
this.applyDerivedMilestoneAcceptanceCriteria(slice.milestoneId);
|
|
11814
11903
|
return feature;
|
|
11815
11904
|
}
|
|
11816
11905
|
/**
|
|
@@ -13180,11 +13269,11 @@ ${feature.acceptanceCriteria}`);
|
|
|
13180
13269
|
applied++;
|
|
13181
13270
|
}
|
|
13182
13271
|
for (const milestone of snapshot.payload.milestones) {
|
|
13183
|
-
this.db.prepare(`INSERT INTO milestones (id, missionId, title, description, status, orderIndex, interviewState, dependencies, planningNotes, verification, validationState, createdAt, updatedAt)
|
|
13184
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
13272
|
+
this.db.prepare(`INSERT INTO milestones (id, missionId, title, description, status, orderIndex, interviewState, dependencies, planningNotes, verification, acceptanceCriteria, validationState, createdAt, updatedAt)
|
|
13273
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
13185
13274
|
ON CONFLICT(id) DO UPDATE SET title=excluded.title, description=excluded.description, status=excluded.status, orderIndex=excluded.orderIndex,
|
|
13186
13275
|
interviewState=excluded.interviewState, dependencies=excluded.dependencies, planningNotes=excluded.planningNotes, verification=excluded.verification,
|
|
13187
|
-
validationState=excluded.validationState, updatedAt=excluded.updatedAt`).run(
|
|
13276
|
+
acceptanceCriteria=excluded.acceptanceCriteria, validationState=excluded.validationState, updatedAt=excluded.updatedAt`).run(
|
|
13188
13277
|
milestone.id,
|
|
13189
13278
|
milestone.missionId,
|
|
13190
13279
|
milestone.title,
|
|
@@ -13195,6 +13284,7 @@ ${feature.acceptanceCriteria}`);
|
|
|
13195
13284
|
toJsonNullable(milestone.dependencies),
|
|
13196
13285
|
milestone.planningNotes ?? null,
|
|
13197
13286
|
milestone.verification ?? null,
|
|
13287
|
+
milestone.acceptanceCriteria ?? null,
|
|
13198
13288
|
milestone.validationState ?? null,
|
|
13199
13289
|
milestone.createdAt,
|
|
13200
13290
|
milestone.updatedAt
|
|
@@ -37226,6 +37316,8 @@ var init_store = __esm({
|
|
|
37226
37316
|
createdAt: row.createdAt,
|
|
37227
37317
|
updatedAt: row.updatedAt,
|
|
37228
37318
|
columnMovedAt: row.columnMovedAt || void 0,
|
|
37319
|
+
firstExecutionAt: row.firstExecutionAt || void 0,
|
|
37320
|
+
cumulativeActiveMs: row.cumulativeActiveMs ?? void 0,
|
|
37229
37321
|
executionStartedAt: row.executionStartedAt || void 0,
|
|
37230
37322
|
executionCompletedAt: row.executionCompletedAt || void 0,
|
|
37231
37323
|
dependencies: fromJson(row.dependencies) || [],
|
|
@@ -37353,6 +37445,8 @@ var init_store = __esm({
|
|
|
37353
37445
|
createdAt: entry.createdAt,
|
|
37354
37446
|
updatedAt: entry.updatedAt,
|
|
37355
37447
|
columnMovedAt: entry.columnMovedAt,
|
|
37448
|
+
firstExecutionAt: entry.firstExecutionAt,
|
|
37449
|
+
cumulativeActiveMs: entry.cumulativeActiveMs,
|
|
37356
37450
|
executionStartedAt: entry.executionStartedAt,
|
|
37357
37451
|
executionCompletedAt: entry.executionCompletedAt,
|
|
37358
37452
|
modelPresetId: entry.modelPresetId,
|
|
@@ -37460,6 +37554,8 @@ ${recentText}` : void 0
|
|
|
37460
37554
|
createdAt: task.createdAt,
|
|
37461
37555
|
updatedAt: task.updatedAt,
|
|
37462
37556
|
columnMovedAt: task.columnMovedAt,
|
|
37557
|
+
firstExecutionAt: task.firstExecutionAt,
|
|
37558
|
+
cumulativeActiveMs: task.cumulativeActiveMs,
|
|
37463
37559
|
executionStartedAt: task.executionStartedAt,
|
|
37464
37560
|
executionCompletedAt: task.executionCompletedAt,
|
|
37465
37561
|
archivedAt,
|
|
@@ -37577,6 +37673,8 @@ ${recentText}` : void 0
|
|
|
37577
37673
|
"createdAt",
|
|
37578
37674
|
"updatedAt",
|
|
37579
37675
|
"columnMovedAt",
|
|
37676
|
+
"firstExecutionAt",
|
|
37677
|
+
"cumulativeActiveMs",
|
|
37580
37678
|
"executionStartedAt",
|
|
37581
37679
|
"executionCompletedAt",
|
|
37582
37680
|
"dependencies",
|
|
@@ -37713,6 +37811,8 @@ ${outcome}`;
|
|
|
37713
37811
|
"createdAt",
|
|
37714
37812
|
"updatedAt",
|
|
37715
37813
|
"columnMovedAt",
|
|
37814
|
+
"firstExecutionAt",
|
|
37815
|
+
"cumulativeActiveMs",
|
|
37716
37816
|
"executionStartedAt",
|
|
37717
37817
|
"executionCompletedAt",
|
|
37718
37818
|
"dependencies",
|
|
@@ -37838,6 +37938,8 @@ ${outcome}`;
|
|
|
37838
37938
|
task.createdAt,
|
|
37839
37939
|
task.updatedAt,
|
|
37840
37940
|
task.columnMovedAt ?? null,
|
|
37941
|
+
task.firstExecutionAt ?? null,
|
|
37942
|
+
task.cumulativeActiveMs ?? null,
|
|
37841
37943
|
task.executionStartedAt ?? null,
|
|
37842
37944
|
task.executionCompletedAt ?? null,
|
|
37843
37945
|
toJson(task.dependencies || []),
|
|
@@ -37902,7 +38004,7 @@ ${outcome}`;
|
|
|
37902
38004
|
workflowStepRetries, stuckKillCount, postReviewFixCount, recoveryRetryCount, taskDoneRetryCount, verificationFailureCount, mergeConflictBounceCount, mergeAuditBounceCount, branchConflictRecoveryCount, reviewerContextRetryCount, reviewerFallbackRetryCount, nextRecoveryAt, error,
|
|
37903
38005
|
summary, thinkingLevel, executionMode, tokenUsageInputTokens, tokenUsageOutputTokens, tokenUsageCachedTokens,
|
|
37904
38006
|
tokenUsageCacheWriteTokens, tokenUsageTotalTokens, tokenUsageFirstUsedAt, tokenUsageLastUsedAt, tokenBudgetSoftAlertedAt, tokenBudgetHardAlertedAt, tokenBudgetOverride, createdAt, updatedAt, columnMovedAt,
|
|
37905
|
-
executionStartedAt, executionCompletedAt,
|
|
38007
|
+
firstExecutionAt, cumulativeActiveMs, executionStartedAt, executionCompletedAt,
|
|
37906
38008
|
dependencies, steps, log, attachments, steeringComments,
|
|
37907
38009
|
comments, review, reviewState, workflowStepResults, prInfo, issueInfo, githubTracking,
|
|
37908
38010
|
sourceIssueProvider, sourceIssueRepository, sourceIssueExternalIssueId, sourceIssueNumber, sourceIssueUrl,
|
|
@@ -37926,7 +38028,7 @@ ${outcome}`;
|
|
|
37926
38028
|
workflowStepRetries, stuckKillCount, postReviewFixCount, recoveryRetryCount, taskDoneRetryCount, verificationFailureCount, mergeConflictBounceCount, mergeAuditBounceCount, branchConflictRecoveryCount, reviewerContextRetryCount, reviewerFallbackRetryCount, nextRecoveryAt, error,
|
|
37927
38029
|
summary, thinkingLevel, executionMode, tokenUsageInputTokens, tokenUsageOutputTokens, tokenUsageCachedTokens,
|
|
37928
38030
|
tokenUsageCacheWriteTokens, tokenUsageTotalTokens, tokenUsageFirstUsedAt, tokenUsageLastUsedAt, tokenBudgetSoftAlertedAt, tokenBudgetHardAlertedAt, tokenBudgetOverride, createdAt, updatedAt, columnMovedAt,
|
|
37929
|
-
executionStartedAt, executionCompletedAt,
|
|
38031
|
+
firstExecutionAt, cumulativeActiveMs, executionStartedAt, executionCompletedAt,
|
|
37930
38032
|
dependencies, steps, log, attachments, steeringComments,
|
|
37931
38033
|
comments, review, reviewState, workflowStepResults, prInfo, issueInfo, githubTracking,
|
|
37932
38034
|
sourceIssueProvider, sourceIssueRepository, sourceIssueExternalIssueId, sourceIssueNumber, sourceIssueUrl,
|
|
@@ -37988,6 +38090,8 @@ ${outcome}`;
|
|
|
37988
38090
|
createdAt = excluded.createdAt,
|
|
37989
38091
|
updatedAt = excluded.updatedAt,
|
|
37990
38092
|
columnMovedAt = excluded.columnMovedAt,
|
|
38093
|
+
firstExecutionAt = excluded.firstExecutionAt,
|
|
38094
|
+
cumulativeActiveMs = excluded.cumulativeActiveMs,
|
|
37991
38095
|
executionStartedAt = excluded.executionStartedAt,
|
|
37992
38096
|
executionCompletedAt = excluded.executionCompletedAt,
|
|
37993
38097
|
dependencies = excluded.dependencies,
|
|
@@ -39645,10 +39749,20 @@ ${newTask.description}
|
|
|
39645
39749
|
task.column = toColumn;
|
|
39646
39750
|
task.columnMovedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
39647
39751
|
task.updatedAt = task.columnMovedAt;
|
|
39648
|
-
if (
|
|
39649
|
-
task.executionStartedAt
|
|
39752
|
+
if (fromColumn === "in-progress" && toColumn !== "in-progress") {
|
|
39753
|
+
const segmentStartMs = Date.parse(task.executionStartedAt ?? task.columnMovedAt);
|
|
39754
|
+
const segmentEndMs = Date.parse(task.columnMovedAt);
|
|
39755
|
+
const segmentDeltaMs = Number.isFinite(segmentStartMs) && Number.isFinite(segmentEndMs) ? Math.max(0, segmentEndMs - segmentStartMs) : 0;
|
|
39756
|
+
task.cumulativeActiveMs = Math.max(0, task.cumulativeActiveMs ?? 0) + segmentDeltaMs;
|
|
39650
39757
|
}
|
|
39651
39758
|
if (toColumn === "in-progress") {
|
|
39759
|
+
task.cumulativeActiveMs ??= 0;
|
|
39760
|
+
if (!task.firstExecutionAt) {
|
|
39761
|
+
task.firstExecutionAt = task.columnMovedAt;
|
|
39762
|
+
}
|
|
39763
|
+
if (!task.executionStartedAt) {
|
|
39764
|
+
task.executionStartedAt = task.columnMovedAt;
|
|
39765
|
+
}
|
|
39652
39766
|
task.userPaused = void 0;
|
|
39653
39767
|
}
|
|
39654
39768
|
if (toColumn === "done" && !task.executionCompletedAt) {
|
|
@@ -40090,6 +40204,16 @@ ${newTask.description}
|
|
|
40090
40204
|
} else if (updates.sessionFile !== void 0) {
|
|
40091
40205
|
task.sessionFile = updates.sessionFile;
|
|
40092
40206
|
}
|
|
40207
|
+
if (updates.firstExecutionAt === null) {
|
|
40208
|
+
task.firstExecutionAt = void 0;
|
|
40209
|
+
} else if (updates.firstExecutionAt !== void 0) {
|
|
40210
|
+
task.firstExecutionAt = updates.firstExecutionAt;
|
|
40211
|
+
}
|
|
40212
|
+
if (updates.cumulativeActiveMs === null) {
|
|
40213
|
+
task.cumulativeActiveMs = void 0;
|
|
40214
|
+
} else if (updates.cumulativeActiveMs !== void 0) {
|
|
40215
|
+
task.cumulativeActiveMs = updates.cumulativeActiveMs;
|
|
40216
|
+
}
|
|
40093
40217
|
if (updates.executionStartedAt === null) {
|
|
40094
40218
|
task.executionStartedAt = void 0;
|
|
40095
40219
|
} else if (updates.executionStartedAt !== void 0) {
|
|
@@ -40221,6 +40345,7 @@ ${newTask.description}
|
|
|
40221
40345
|
}
|
|
40222
40346
|
if (!paused) {
|
|
40223
40347
|
task.pausedByAgentId = void 0;
|
|
40348
|
+
task.userPaused = void 0;
|
|
40224
40349
|
}
|
|
40225
40350
|
if (task.column === "in-progress" || task.column === "in-review") {
|
|
40226
40351
|
task.status = paused ? "paused" : void 0;
|
|
@@ -60259,13 +60384,15 @@ var init_chat_store = __esm({
|
|
|
60259
60384
|
whereClauses.push("createdAt < ?");
|
|
60260
60385
|
params.push(filter2.before);
|
|
60261
60386
|
}
|
|
60387
|
+
const order = filter2?.order === "desc" ? "DESC" : "ASC";
|
|
60262
60388
|
const rows = this.db.prepare(`
|
|
60263
60389
|
SELECT * FROM chat_room_messages
|
|
60264
60390
|
WHERE ${whereClauses.join(" AND ")}
|
|
60265
|
-
ORDER BY createdAt
|
|
60391
|
+
ORDER BY createdAt ${order}
|
|
60266
60392
|
LIMIT ? OFFSET ?
|
|
60267
60393
|
`).all(...params, filter2?.limit ?? 100, filter2?.offset ?? 0);
|
|
60268
|
-
|
|
60394
|
+
const normalizedRows = filter2?.order === "desc" ? [...rows].reverse() : rows;
|
|
60395
|
+
return normalizedRows.map((row) => this.rowToRoomMessage(row));
|
|
60269
60396
|
}
|
|
60270
60397
|
listRoomMessagesSince(roomId, sinceIso, options) {
|
|
60271
60398
|
const whereClauses = ["roomId = ?", "createdAt > ?"];
|
|
@@ -60546,6 +60673,8 @@ var src_exports = {};
|
|
|
60546
60673
|
__export(src_exports, {
|
|
60547
60674
|
AGENT_PERMISSIONS: () => AGENT_PERMISSIONS,
|
|
60548
60675
|
AGENT_PERMISSION_POLICY_ACTION_CATEGORIES: () => AGENT_PERMISSION_POLICY_ACTION_CATEGORIES,
|
|
60676
|
+
AGENT_PERMISSION_POLICY_CATEGORY_TOOL_EXAMPLES: () => AGENT_PERMISSION_POLICY_CATEGORY_TOOL_EXAMPLES,
|
|
60677
|
+
AGENT_PERMISSION_POLICY_EXEMPT_TOOL_EXAMPLES: () => AGENT_PERMISSION_POLICY_EXEMPT_TOOL_EXAMPLES,
|
|
60549
60678
|
AGENT_PERMISSION_POLICY_PRESET_IDS: () => AGENT_PERMISSION_POLICY_PRESET_IDS,
|
|
60550
60679
|
AGENT_PROVISIONING_APPROVAL_MODES: () => AGENT_PROVISIONING_APPROVAL_MODES,
|
|
60551
60680
|
AGENT_VALID_TRANSITIONS: () => AGENT_VALID_TRANSITIONS,
|
|
@@ -64764,6 +64893,7 @@ var init_gating_classifications = __esm({
|
|
|
64764
64893
|
"fn_slice_activate",
|
|
64765
64894
|
"fn_feature_link_task",
|
|
64766
64895
|
"fn_feature_update",
|
|
64896
|
+
"fn_milestone_update",
|
|
64767
64897
|
"fn_agent_stop",
|
|
64768
64898
|
"fn_agent_start"
|
|
64769
64899
|
];
|
|
@@ -64784,9 +64914,15 @@ var init_gating_classifications = __esm({
|
|
|
64784
64914
|
NETWORK_API_TOOLS = /* @__PURE__ */ new Set([
|
|
64785
64915
|
"fn_research_run",
|
|
64786
64916
|
"fn_research_cancel",
|
|
64787
|
-
"fn_research_retry"
|
|
64917
|
+
"fn_research_retry",
|
|
64918
|
+
"fn_web_fetch"
|
|
64919
|
+
// FN-4603: outbound HTTP fetch should be network-classified.
|
|
64920
|
+
]);
|
|
64921
|
+
ACTION_GATE_NETWORK_API_TOOLS = /* @__PURE__ */ new Set([
|
|
64922
|
+
"fn_research_run",
|
|
64923
|
+
"fn_web_fetch"
|
|
64924
|
+
// FN-4603: honor network_api approval policy for web fetches.
|
|
64788
64925
|
]);
|
|
64789
|
-
ACTION_GATE_NETWORK_API_TOOLS = /* @__PURE__ */ new Set(["fn_research_run"]);
|
|
64790
64926
|
READONLY_FN_TOOLS = /* @__PURE__ */ new Set([
|
|
64791
64927
|
"fn_task_list",
|
|
64792
64928
|
"fn_task_show",
|
|
@@ -86590,6 +86726,91 @@ var init_auto_recovery = __esm({
|
|
|
86590
86726
|
}
|
|
86591
86727
|
});
|
|
86592
86728
|
|
|
86729
|
+
// ../engine/src/restart-recovery-coordinator.ts
|
|
86730
|
+
function hasStepProgress(task) {
|
|
86731
|
+
const steps = Array.isArray(task.steps) ? task.steps : [];
|
|
86732
|
+
return steps.some((step) => step.status === "done" || step.status === "in-progress" || step.status === "skipped");
|
|
86733
|
+
}
|
|
86734
|
+
function isNoTaskDoneFailure(task) {
|
|
86735
|
+
return task.status === "failed" && typeof task.error === "string" && task.error.toLowerCase().includes("without calling fn_task_done");
|
|
86736
|
+
}
|
|
86737
|
+
function findMissingWorktreeSessionPrefix(error) {
|
|
86738
|
+
for (const prefix of MISSING_WORKTREE_SESSION_PREFIXES) {
|
|
86739
|
+
if (error.includes(prefix)) {
|
|
86740
|
+
return prefix;
|
|
86741
|
+
}
|
|
86742
|
+
}
|
|
86743
|
+
return null;
|
|
86744
|
+
}
|
|
86745
|
+
function isMissingWorktreeSessionStartFailure(error) {
|
|
86746
|
+
if (typeof error !== "string") {
|
|
86747
|
+
return false;
|
|
86748
|
+
}
|
|
86749
|
+
return findMissingWorktreeSessionPrefix(error) !== null;
|
|
86750
|
+
}
|
|
86751
|
+
function extractMissingWorktreePathFromSessionStartFailure(error) {
|
|
86752
|
+
if (typeof error !== "string") return null;
|
|
86753
|
+
const prefix = findMissingWorktreeSessionPrefix(error);
|
|
86754
|
+
if (!prefix) return null;
|
|
86755
|
+
const idx = error.indexOf(prefix);
|
|
86756
|
+
const pathPart = error.slice(idx + prefix.length).trim();
|
|
86757
|
+
return pathPart.length > 0 ? pathPart : null;
|
|
86758
|
+
}
|
|
86759
|
+
function isRecoverableMissingWorktreeReviewFailure(task) {
|
|
86760
|
+
return task.column === "in-review" && !task.paused && task.status === "failed" && isMissingWorktreeSessionStartFailure(task.error) && hasStepProgress(task);
|
|
86761
|
+
}
|
|
86762
|
+
var log14, MISSING_WORKTREE_SESSION_PREFIXES, RestartRecoveryCoordinator;
|
|
86763
|
+
var init_restart_recovery_coordinator = __esm({
|
|
86764
|
+
"../engine/src/restart-recovery-coordinator.ts"() {
|
|
86765
|
+
"use strict";
|
|
86766
|
+
init_logger2();
|
|
86767
|
+
log14 = createLogger2("restart-recovery");
|
|
86768
|
+
MISSING_WORKTREE_SESSION_PREFIXES = [
|
|
86769
|
+
"Refusing to start coding agent in missing worktree:",
|
|
86770
|
+
"Refusing to start coding agent in incomplete worktree:",
|
|
86771
|
+
"Refusing to start coding agent in unregistered git worktree:"
|
|
86772
|
+
];
|
|
86773
|
+
RestartRecoveryCoordinator = class {
|
|
86774
|
+
constructor(store, executor) {
|
|
86775
|
+
this.store = store;
|
|
86776
|
+
this.executor = executor;
|
|
86777
|
+
}
|
|
86778
|
+
async recoverInterruptedRuns() {
|
|
86779
|
+
const allInProgress = await this.store.listTasks({ slim: true, column: "in-progress" });
|
|
86780
|
+
const candidates = allInProgress.filter((task) => task.column === "in-progress" && !task.paused);
|
|
86781
|
+
if (candidates.length === 0) return;
|
|
86782
|
+
let requeued = 0;
|
|
86783
|
+
for (const task of candidates) {
|
|
86784
|
+
if (!this.mustSafeRetry(task)) continue;
|
|
86785
|
+
await this.safeRequeue(task);
|
|
86786
|
+
requeued++;
|
|
86787
|
+
}
|
|
86788
|
+
if (requeued > 0) {
|
|
86789
|
+
log14.log(`Restart recovery requeued ${requeued} interrupted task(s) for safe retry`);
|
|
86790
|
+
}
|
|
86791
|
+
await this.executor.resumeOrphaned();
|
|
86792
|
+
}
|
|
86793
|
+
mustSafeRetry(task) {
|
|
86794
|
+
return isNoTaskDoneFailure(task) && !hasStepProgress(task);
|
|
86795
|
+
}
|
|
86796
|
+
async safeRequeue(task) {
|
|
86797
|
+
await this.store.updateTask(task.id, {
|
|
86798
|
+
status: "stuck-killed",
|
|
86799
|
+
worktree: null,
|
|
86800
|
+
branch: null,
|
|
86801
|
+
sessionFile: null,
|
|
86802
|
+
error: null
|
|
86803
|
+
});
|
|
86804
|
+
await this.store.logEntry(
|
|
86805
|
+
task.id,
|
|
86806
|
+
"Restart recovery: interrupted run had no step progress and no fn_task_done \u2014 requeued to todo for safe retry"
|
|
86807
|
+
);
|
|
86808
|
+
await this.store.moveTask(task.id, "todo");
|
|
86809
|
+
}
|
|
86810
|
+
};
|
|
86811
|
+
}
|
|
86812
|
+
});
|
|
86813
|
+
|
|
86593
86814
|
// ../engine/src/auto-recovery-handlers/branch-worktree.ts
|
|
86594
86815
|
import { exec as exec9 } from "node:child_process";
|
|
86595
86816
|
import { existsSync as existsSync32 } from "node:fs";
|
|
@@ -87763,6 +87984,7 @@ var init_executor = __esm({
|
|
|
87763
87984
|
init_prompt_layers();
|
|
87764
87985
|
init_run_audit();
|
|
87765
87986
|
init_auto_recovery();
|
|
87987
|
+
init_restart_recovery_coordinator();
|
|
87766
87988
|
init_branch_worktree();
|
|
87767
87989
|
init_contamination();
|
|
87768
87990
|
init_file_scope();
|
|
@@ -90446,7 +90668,22 @@ ${summary}`,
|
|
|
90446
90668
|
this.options.onComplete?.(task);
|
|
90447
90669
|
} else {
|
|
90448
90670
|
let taskDoneSessionRetries = 0;
|
|
90671
|
+
let retryAbortedDueToReclaim = false;
|
|
90449
90672
|
while (!taskDone && taskDoneSessionRetries < MAX_TASK_DONE_SESSION_RETRIES) {
|
|
90673
|
+
const liveTask = await this.store.getTask(task.id);
|
|
90674
|
+
const hasExplicitWorktreeBinding = typeof liveTask.worktree === "string" || liveTask.worktree === null;
|
|
90675
|
+
const hasExplicitBranchBinding = typeof liveTask.branch === "string" || liveTask.branch === null;
|
|
90676
|
+
const worktreeContractIntact = liveTask.column === "in-progress" && !liveTask.paused && (!hasExplicitWorktreeBinding || liveTask.worktree === worktreePath) && (!hasExplicitBranchBinding || typeof liveTask.branch === "string" && liveTask.branch.length > 0);
|
|
90677
|
+
if (!worktreeContractIntact) {
|
|
90678
|
+
const reclaimMessage = `${task.id}: worktree/branch reclaimed during no-fn_task_done retry \u2014 aborting retry and requeueing`;
|
|
90679
|
+
executorLog.log(reclaimMessage);
|
|
90680
|
+
await this.store.logEntry(task.id, reclaimMessage, void 0, this.currentRunContext);
|
|
90681
|
+
this.activeSessions.delete(task.id);
|
|
90682
|
+
this.tokenUsageBaselines.delete(task.id);
|
|
90683
|
+
session.dispose();
|
|
90684
|
+
retryAbortedDueToReclaim = true;
|
|
90685
|
+
break;
|
|
90686
|
+
}
|
|
90450
90687
|
taskDoneSessionRetries++;
|
|
90451
90688
|
executorLog.log(
|
|
90452
90689
|
`\u26A0 ${task.id} finished without fn_task_done \u2014 retrying with new session (${taskDoneSessionRetries}/${MAX_TASK_DONE_SESSION_RETRIES})`
|
|
@@ -90473,88 +90710,112 @@ ${summary}`,
|
|
|
90473
90710
|
this.activeSessions.delete(task.id);
|
|
90474
90711
|
this.tokenUsageBaselines.delete(task.id);
|
|
90475
90712
|
session.dispose();
|
|
90476
|
-
|
|
90477
|
-
|
|
90478
|
-
|
|
90479
|
-
|
|
90480
|
-
|
|
90481
|
-
|
|
90482
|
-
|
|
90483
|
-
|
|
90484
|
-
|
|
90485
|
-
|
|
90486
|
-
|
|
90487
|
-
|
|
90488
|
-
|
|
90489
|
-
|
|
90490
|
-
|
|
90491
|
-
|
|
90492
|
-
|
|
90493
|
-
|
|
90494
|
-
|
|
90495
|
-
|
|
90496
|
-
|
|
90497
|
-
|
|
90498
|
-
|
|
90499
|
-
|
|
90500
|
-
|
|
90501
|
-
|
|
90502
|
-
this.store.updateTask(task.id, { sessionFile: retrySessionFile }).catch((err) => {
|
|
90503
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
90504
|
-
executorLog.warn(`${task.id} failed to persist retry sessionFile: ${msg}`);
|
|
90713
|
+
let retrySession2 = null;
|
|
90714
|
+
try {
|
|
90715
|
+
const createdRetrySession = await createResolvedAgentSession({
|
|
90716
|
+
sessionPurpose: "executor",
|
|
90717
|
+
runtimeHint: executorRuntimeHint,
|
|
90718
|
+
pluginRunner: this.options.pluginRunner,
|
|
90719
|
+
cwd: worktreePath,
|
|
90720
|
+
systemPrompt: executorSystemPromptFinal,
|
|
90721
|
+
systemPromptLayers: executorLayers,
|
|
90722
|
+
tools: "coding",
|
|
90723
|
+
customTools,
|
|
90724
|
+
onText: agentLogger.onText,
|
|
90725
|
+
onThinking: agentLogger.onThinking,
|
|
90726
|
+
onToolStart: agentLogger.onToolStart,
|
|
90727
|
+
onToolEnd: agentLogger.onToolEnd,
|
|
90728
|
+
defaultProvider: executorProvider,
|
|
90729
|
+
defaultModelId: executorModelId,
|
|
90730
|
+
fallbackProvider: executorFallbackProvider,
|
|
90731
|
+
fallbackModelId: executorFallbackModelId,
|
|
90732
|
+
defaultThinkingLevel: executorThinkingLevel,
|
|
90733
|
+
sessionManager: SessionManager2.create(worktreePath),
|
|
90734
|
+
taskEnv,
|
|
90735
|
+
// Skill selection: use assigned agent skills if available, otherwise role fallback
|
|
90736
|
+
...skillContext.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {},
|
|
90737
|
+
actionGateContext: this.buildActionGateContext(task.id, assignedAgent, settings.defaultAgentPermissionPolicy),
|
|
90738
|
+
permanentAgentGating: this.buildPermanentAgentGatingContext(task.id, assignedAgent, settings.defaultAgentPermissionPolicy)
|
|
90505
90739
|
});
|
|
90740
|
+
retrySession2 = createdRetrySession.session;
|
|
90741
|
+
if (createdRetrySession.sessionFile) {
|
|
90742
|
+
this.store.updateTask(task.id, { sessionFile: createdRetrySession.sessionFile }).catch((err) => {
|
|
90743
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
90744
|
+
executorLog.warn(`${task.id} failed to persist retry sessionFile: ${msg}`);
|
|
90745
|
+
});
|
|
90746
|
+
}
|
|
90747
|
+
session = retrySession2;
|
|
90748
|
+
sessionRef.current = retrySession2;
|
|
90749
|
+
this.activeSessions.set(task.id, {
|
|
90750
|
+
session: retrySession2,
|
|
90751
|
+
seenSteeringIds,
|
|
90752
|
+
lastResolvedModelProvider: executorProvider,
|
|
90753
|
+
lastResolvedModelId: executorModelId,
|
|
90754
|
+
lastTaskModelProvider: detail.modelProvider,
|
|
90755
|
+
lastTaskModelId: detail.modelId,
|
|
90756
|
+
lastAssignedAgentId: detail.assignedAgentId ?? null
|
|
90757
|
+
});
|
|
90758
|
+
stuckDetector?.trackTask(task.id, retrySession2);
|
|
90759
|
+
let retryPrompt;
|
|
90760
|
+
if (pseudoPause.kind !== "none") {
|
|
90761
|
+
const shortMatch = (pseudoPause.matched ?? "").slice(0, 120);
|
|
90762
|
+
retryPrompt = [
|
|
90763
|
+
`Your previous turn ended with a pseudo-pause: "${shortMatch}". This is forbidden.`,
|
|
90764
|
+
"",
|
|
90765
|
+
"Turn-ending rules you violated:",
|
|
90766
|
+
"- You MUST NOT end a turn by asking the user a question, summarizing progress, or requesting permission to continue.",
|
|
90767
|
+
"- Phrases like 'If you want, I can continue', 'Should I proceed?', 'Let me know if...' are FORBIDDEN turn-endings.",
|
|
90768
|
+
"- The user is not watching this conversation. Questions written as prose are ignored.",
|
|
90769
|
+
"- If you genuinely cannot proceed, call fn_task_done with a clear explanation \u2014 never write the blocker as plain prose.",
|
|
90770
|
+
"",
|
|
90771
|
+
"What you must do now:",
|
|
90772
|
+
"1. Review the PROMPT.md steps and identify the next pending step.",
|
|
90773
|
+
"2. Do the work for that step immediately \u2014 call fn_task_update, write code, run tests.",
|
|
90774
|
+
"3. Continue until all steps are done, then call fn_task_done.",
|
|
90775
|
+
"Do NOT ask for permission. Do NOT write a summary. Just call a tool and keep working.",
|
|
90776
|
+
"",
|
|
90777
|
+
"Original task:",
|
|
90778
|
+
buildExecutionPrompt(detail, this.rootDir, settings, worktreePath, this.options.pluginRunner)
|
|
90779
|
+
].join("\n");
|
|
90780
|
+
} else {
|
|
90781
|
+
retryPrompt = [
|
|
90782
|
+
"Your previous session ended without calling the fn_task_done tool.",
|
|
90783
|
+
"The task may already be complete \u2014 review the current state of the worktree and either:",
|
|
90784
|
+
"1. If the work is done, call fn_task_done with a summary of what was accomplished.",
|
|
90785
|
+
"2. If there is remaining work, finish it and then call fn_task_done.",
|
|
90786
|
+
"",
|
|
90787
|
+
"Original task:",
|
|
90788
|
+
buildExecutionPrompt(detail, this.rootDir, settings, worktreePath, this.options.pluginRunner)
|
|
90789
|
+
].join("\n");
|
|
90790
|
+
}
|
|
90791
|
+
stuckDetector?.recordActivity(task.id);
|
|
90792
|
+
await promptWithFallback(retrySession2, retryPrompt);
|
|
90793
|
+
checkSessionError(retrySession2);
|
|
90794
|
+
await accumulateSessionTokenUsage(this.store, task.id, retrySession2, {
|
|
90795
|
+
agentId: task.assignedAgentId ?? void 0,
|
|
90796
|
+
role: "executor"
|
|
90797
|
+
});
|
|
90798
|
+
} catch (retryError) {
|
|
90799
|
+
const retryErrorText = retryError instanceof Error ? retryError.message : String(retryError);
|
|
90800
|
+
if (!isMissingWorktreeSessionStartFailure(retryErrorText)) {
|
|
90801
|
+
throw retryError;
|
|
90802
|
+
}
|
|
90803
|
+
const recoveredPath = extractMissingWorktreePathFromSessionStartFailure(retryErrorText) ?? worktreePath;
|
|
90804
|
+
const reclaimMessage = `${task.id}: no-fn_task_done retry hit missing worktree session-start failure (${recoveredPath}) \u2014 clearing stale metadata and requeueing`;
|
|
90805
|
+
executorLog.log(reclaimMessage);
|
|
90806
|
+
await this.store.logEntry(task.id, reclaimMessage, void 0, this.currentRunContext);
|
|
90807
|
+
await this.store.updateTask(task.id, {
|
|
90808
|
+
sessionFile: null,
|
|
90809
|
+
worktree: null,
|
|
90810
|
+
branch: null,
|
|
90811
|
+
baseCommitSha: null
|
|
90812
|
+
});
|
|
90813
|
+
this.activeSessions.delete(task.id);
|
|
90814
|
+
this.tokenUsageBaselines.delete(task.id);
|
|
90815
|
+
retrySession2?.dispose();
|
|
90816
|
+
retryAbortedDueToReclaim = true;
|
|
90817
|
+
break;
|
|
90506
90818
|
}
|
|
90507
|
-
session = retrySession2;
|
|
90508
|
-
sessionRef.current = retrySession2;
|
|
90509
|
-
this.activeSessions.set(task.id, {
|
|
90510
|
-
session: retrySession2,
|
|
90511
|
-
seenSteeringIds,
|
|
90512
|
-
lastResolvedModelProvider: executorProvider,
|
|
90513
|
-
lastResolvedModelId: executorModelId,
|
|
90514
|
-
lastTaskModelProvider: detail.modelProvider,
|
|
90515
|
-
lastTaskModelId: detail.modelId,
|
|
90516
|
-
lastAssignedAgentId: detail.assignedAgentId ?? null
|
|
90517
|
-
});
|
|
90518
|
-
stuckDetector?.trackTask(task.id, retrySession2);
|
|
90519
|
-
let retryPrompt;
|
|
90520
|
-
if (pseudoPause.kind !== "none") {
|
|
90521
|
-
const shortMatch = (pseudoPause.matched ?? "").slice(0, 120);
|
|
90522
|
-
retryPrompt = [
|
|
90523
|
-
`Your previous turn ended with a pseudo-pause: "${shortMatch}". This is forbidden.`,
|
|
90524
|
-
"",
|
|
90525
|
-
"Turn-ending rules you violated:",
|
|
90526
|
-
"- You MUST NOT end a turn by asking the user a question, summarizing progress, or requesting permission to continue.",
|
|
90527
|
-
"- Phrases like 'If you want, I can continue', 'Should I proceed?', 'Let me know if...' are FORBIDDEN turn-endings.",
|
|
90528
|
-
"- The user is not watching this conversation. Questions written as prose are ignored.",
|
|
90529
|
-
"- If you genuinely cannot proceed, call fn_task_done with a clear explanation \u2014 never write the blocker as plain prose.",
|
|
90530
|
-
"",
|
|
90531
|
-
"What you must do now:",
|
|
90532
|
-
"1. Review the PROMPT.md steps and identify the next pending step.",
|
|
90533
|
-
"2. Do the work for that step immediately \u2014 call fn_task_update, write code, run tests.",
|
|
90534
|
-
"3. Continue until all steps are done, then call fn_task_done.",
|
|
90535
|
-
"Do NOT ask for permission. Do NOT write a summary. Just call a tool and keep working.",
|
|
90536
|
-
"",
|
|
90537
|
-
"Original task:",
|
|
90538
|
-
buildExecutionPrompt(detail, this.rootDir, settings, worktreePath, this.options.pluginRunner)
|
|
90539
|
-
].join("\n");
|
|
90540
|
-
} else {
|
|
90541
|
-
retryPrompt = [
|
|
90542
|
-
"Your previous session ended without calling the fn_task_done tool.",
|
|
90543
|
-
"The task may already be complete \u2014 review the current state of the worktree and either:",
|
|
90544
|
-
"1. If the work is done, call fn_task_done with a summary of what was accomplished.",
|
|
90545
|
-
"2. If there is remaining work, finish it and then call fn_task_done.",
|
|
90546
|
-
"",
|
|
90547
|
-
"Original task:",
|
|
90548
|
-
buildExecutionPrompt(detail, this.rootDir, settings, worktreePath, this.options.pluginRunner)
|
|
90549
|
-
].join("\n");
|
|
90550
|
-
}
|
|
90551
|
-
stuckDetector?.recordActivity(task.id);
|
|
90552
|
-
await promptWithFallback(retrySession2, retryPrompt);
|
|
90553
|
-
checkSessionError(retrySession2);
|
|
90554
|
-
await accumulateSessionTokenUsage(this.store, task.id, retrySession2, {
|
|
90555
|
-
agentId: task.assignedAgentId ?? void 0,
|
|
90556
|
-
role: "executor"
|
|
90557
|
-
});
|
|
90558
90819
|
if (!taskDone) {
|
|
90559
90820
|
const implicitCheck = await this.store.getTask(task.id);
|
|
90560
90821
|
if (implicitCheck.steps.length > 0 && implicitCheck.steps.every((s) => s.status === "done" || s.status === "skipped")) {
|
|
@@ -90617,7 +90878,7 @@ ${summary}`,
|
|
|
90617
90878
|
} else {
|
|
90618
90879
|
const priorRequeues = task.taskDoneRetryCount ?? 0;
|
|
90619
90880
|
const nextRequeueCount = priorRequeues + 1;
|
|
90620
|
-
const errorMessage = `Agent finished without calling fn_task_done (after ${MAX_TASK_DONE_SESSION_RETRIES} retries)`;
|
|
90881
|
+
const errorMessage = retryAbortedDueToReclaim ? "Worktree/branch reclaimed during no-fn_task_done retry \u2014 requeueing" : `Agent finished without calling fn_task_done (after ${MAX_TASK_DONE_SESSION_RETRIES} retries)`;
|
|
90621
90882
|
if (priorRequeues < MAX_TASK_DONE_REQUEUE_RETRIES) {
|
|
90622
90883
|
await this.store.updateTask(task.id, {
|
|
90623
90884
|
status: "failed",
|
|
@@ -102943,7 +103204,7 @@ function extractJson(raw) {
|
|
|
102943
103204
|
if (first >= 0 && last > first) return trimmed.slice(first, last + 1);
|
|
102944
103205
|
return trimmed;
|
|
102945
103206
|
}
|
|
102946
|
-
var
|
|
103207
|
+
var log15, HybridEvaluatorService;
|
|
102947
103208
|
var init_evaluator = __esm({
|
|
102948
103209
|
"../engine/src/evaluator.ts"() {
|
|
102949
103210
|
"use strict";
|
|
@@ -102952,7 +103213,7 @@ var init_evaluator = __esm({
|
|
|
102952
103213
|
init_eval_followups();
|
|
102953
103214
|
init_pi();
|
|
102954
103215
|
init_logger2();
|
|
102955
|
-
|
|
103216
|
+
log15 = createLogger2("evaluator");
|
|
102956
103217
|
HybridEvaluatorService = class {
|
|
102957
103218
|
constructor(deps) {
|
|
102958
103219
|
this.deps = deps;
|
|
@@ -103044,7 +103305,7 @@ var init_evaluator = __esm({
|
|
|
103044
103305
|
try {
|
|
103045
103306
|
session.dispose();
|
|
103046
103307
|
} catch (error) {
|
|
103047
|
-
|
|
103308
|
+
log15.warn(`Evaluator session disposal failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
103048
103309
|
}
|
|
103049
103310
|
}
|
|
103050
103311
|
}
|
|
@@ -103176,7 +103437,7 @@ function truncateOutput2(stdout, stderr) {
|
|
|
103176
103437
|
}
|
|
103177
103438
|
return combined;
|
|
103178
103439
|
}
|
|
103179
|
-
var
|
|
103440
|
+
var log16, FUSION_BINARY_TOKENS, SHELL_METACHARACTERS_REGEX, DEFAULT_TIMEOUT_MS8, MAX_BUFFER, MAX_OUTPUT_LENGTH, DEFAULT_POLL_INTERVAL_MS, MIN_POLL_INTERVAL_MS, CronRunner, AI_AUTOMATION_SYSTEM_PROMPT;
|
|
103180
103441
|
var init_cron_runner = __esm({
|
|
103181
103442
|
"../engine/src/cron-runner.ts"() {
|
|
103182
103443
|
"use strict";
|
|
@@ -103185,7 +103446,7 @@ var init_cron_runner = __esm({
|
|
|
103185
103446
|
init_shell_utils();
|
|
103186
103447
|
init_pi();
|
|
103187
103448
|
init_evaluator();
|
|
103188
|
-
|
|
103449
|
+
log16 = createLogger2("cron-runner");
|
|
103189
103450
|
FUSION_BINARY_TOKENS = /* @__PURE__ */ new Set([
|
|
103190
103451
|
"fn",
|
|
103191
103452
|
"fusion",
|
|
@@ -103226,7 +103487,7 @@ var init_cron_runner = __esm({
|
|
|
103226
103487
|
start() {
|
|
103227
103488
|
if (this.running) return;
|
|
103228
103489
|
this.running = true;
|
|
103229
|
-
|
|
103490
|
+
log16.log(`Started (poll every ${this.pollIntervalMs / 1e3}s, scope: ${this.scope})`);
|
|
103230
103491
|
void this.tick();
|
|
103231
103492
|
this.pollInterval = setInterval(() => {
|
|
103232
103493
|
void this.tick();
|
|
@@ -103240,7 +103501,7 @@ var init_cron_runner = __esm({
|
|
|
103240
103501
|
clearInterval(this.pollInterval);
|
|
103241
103502
|
this.pollInterval = null;
|
|
103242
103503
|
}
|
|
103243
|
-
|
|
103504
|
+
log16.log("Stopped");
|
|
103244
103505
|
}
|
|
103245
103506
|
/**
|
|
103246
103507
|
* Single poll cycle: find due schedules and execute them.
|
|
@@ -103264,29 +103525,29 @@ var init_cron_runner = __esm({
|
|
|
103264
103525
|
const executedIds = /* @__PURE__ */ new Set();
|
|
103265
103526
|
for (const schedule of dueSchedules) {
|
|
103266
103527
|
if (this.inFlight.has(schedule.id)) {
|
|
103267
|
-
|
|
103528
|
+
log16.warn(`Skipping ${schedule.name} (${schedule.id}) \u2014 still running from previous tick`);
|
|
103268
103529
|
continue;
|
|
103269
103530
|
}
|
|
103270
103531
|
if (executedIds.has(schedule.id)) {
|
|
103271
|
-
|
|
103532
|
+
log16.log(`Skipping ${schedule.name} (${schedule.id}) \u2014 already executed from another scope this tick`);
|
|
103272
103533
|
continue;
|
|
103273
103534
|
}
|
|
103274
103535
|
executedIds.add(schedule.id);
|
|
103275
103536
|
const scheduleScope = schedule.scope ?? "project";
|
|
103276
103537
|
if (scheduleScope !== this.scope && this.scope !== "all") {
|
|
103277
|
-
|
|
103538
|
+
log16.log(`Skipping ${schedule.name} (${schedule.id}) \u2014 belongs to ${scheduleScope} scope, not polling`);
|
|
103278
103539
|
continue;
|
|
103279
103540
|
}
|
|
103280
|
-
|
|
103541
|
+
log16.log(`Executing ${schedule.name} (${schedule.id}) [scope: ${scheduleScope}]`);
|
|
103281
103542
|
const currentSettings = await this.store.getSettings();
|
|
103282
103543
|
if (currentSettings.globalPause || currentSettings.enginePaused) {
|
|
103283
|
-
|
|
103544
|
+
log16.log("Pause detected mid-tick \u2014 stopping schedule execution");
|
|
103284
103545
|
break;
|
|
103285
103546
|
}
|
|
103286
103547
|
await this.executeSchedule(schedule);
|
|
103287
103548
|
}
|
|
103288
103549
|
} catch (err) {
|
|
103289
|
-
|
|
103550
|
+
log16.error(`Tick error: ${err.message}`);
|
|
103290
103551
|
} finally {
|
|
103291
103552
|
this.ticking = false;
|
|
103292
103553
|
}
|
|
@@ -103316,13 +103577,13 @@ var init_cron_runner = __esm({
|
|
|
103316
103577
|
try {
|
|
103317
103578
|
await this.automationStore.recordRun(schedule.id, result);
|
|
103318
103579
|
} catch (recordErr) {
|
|
103319
|
-
|
|
103580
|
+
log16.error(`Failed to record run for ${schedule.id}: ${recordErr.message}`);
|
|
103320
103581
|
}
|
|
103321
103582
|
if (this.onScheduleRunProcessed) {
|
|
103322
103583
|
try {
|
|
103323
103584
|
await this.onScheduleRunProcessed(schedule, result);
|
|
103324
103585
|
} catch (callbackErr) {
|
|
103325
|
-
|
|
103586
|
+
log16.error(
|
|
103326
103587
|
`Post-run callback failed for ${schedule.name} (${schedule.id}): ${callbackErr.message}`
|
|
103327
103588
|
);
|
|
103328
103589
|
}
|
|
@@ -103333,7 +103594,7 @@ var init_cron_runner = __esm({
|
|
|
103333
103594
|
* Execute a legacy single-command schedule.
|
|
103334
103595
|
*/
|
|
103335
103596
|
async executeLegacyCommand(schedule, startedAt) {
|
|
103336
|
-
|
|
103597
|
+
log16.log(`Executing ${schedule.name} (${schedule.id}): ${schedule.command}`);
|
|
103337
103598
|
if (isInProcessBackupCommand(schedule.command)) {
|
|
103338
103599
|
return this.executeBackupInProcess(schedule, startedAt);
|
|
103339
103600
|
}
|
|
@@ -103351,7 +103612,7 @@ var init_cron_runner = __esm({
|
|
|
103351
103612
|
shell: defaultShell
|
|
103352
103613
|
});
|
|
103353
103614
|
const output = truncateOutput2(stdout, stderr);
|
|
103354
|
-
|
|
103615
|
+
log16.log(`\u2713 ${schedule.name} completed (${output.length} bytes output)`);
|
|
103355
103616
|
return {
|
|
103356
103617
|
success: true,
|
|
103357
103618
|
output,
|
|
@@ -103363,7 +103624,7 @@ var init_cron_runner = __esm({
|
|
|
103363
103624
|
const stderr = err.stderr ?? "";
|
|
103364
103625
|
const output = truncateOutput2(stdout, stderr);
|
|
103365
103626
|
const errorMessage = err.killed ? `Command timed out after ${(schedule.timeoutMs ?? DEFAULT_TIMEOUT_MS8) / 1e3}s` : err.message ?? String(err);
|
|
103366
|
-
|
|
103627
|
+
log16.warn(`\u2717 ${schedule.name} failed: ${errorMessage}`);
|
|
103367
103628
|
return {
|
|
103368
103629
|
success: false,
|
|
103369
103630
|
output,
|
|
@@ -103381,9 +103642,9 @@ var init_cron_runner = __esm({
|
|
|
103381
103642
|
async executeBackupInProcess(schedule, startedAt) {
|
|
103382
103643
|
const action = await this.runBackupActionInProcess();
|
|
103383
103644
|
if (action.success) {
|
|
103384
|
-
|
|
103645
|
+
log16.log(`\u2713 ${schedule.name} completed in-process`);
|
|
103385
103646
|
} else {
|
|
103386
|
-
|
|
103647
|
+
log16.warn(`\u2717 ${schedule.name} in-process backup ${action.error ? `threw: ${action.error}` : `reported failure: ${action.output}`}`);
|
|
103387
103648
|
}
|
|
103388
103649
|
return {
|
|
103389
103650
|
success: action.success,
|
|
@@ -103396,9 +103657,9 @@ var init_cron_runner = __esm({
|
|
|
103396
103657
|
async executeMemoryBackupInProcess(schedule, startedAt) {
|
|
103397
103658
|
const action = await this.runMemoryBackupActionInProcess();
|
|
103398
103659
|
if (action.success) {
|
|
103399
|
-
|
|
103660
|
+
log16.log(`\u2713 ${schedule.name} completed in-process`);
|
|
103400
103661
|
} else {
|
|
103401
|
-
|
|
103662
|
+
log16.warn(`\u2717 ${schedule.name} in-process memory backup ${action.error ? `threw: ${action.error}` : `reported failure: ${action.output}`}`);
|
|
103402
103663
|
}
|
|
103403
103664
|
return {
|
|
103404
103665
|
success: action.success,
|
|
@@ -103487,25 +103748,25 @@ var init_cron_runner = __esm({
|
|
|
103487
103748
|
*/
|
|
103488
103749
|
async executeSteps(schedule, startedAt) {
|
|
103489
103750
|
const steps = schedule.steps;
|
|
103490
|
-
|
|
103751
|
+
log16.log(`Executing ${schedule.name} (${schedule.id}): ${steps.length} steps`);
|
|
103491
103752
|
const stepResults = [];
|
|
103492
103753
|
let overallSuccess = true;
|
|
103493
103754
|
let stoppedEarly = false;
|
|
103494
103755
|
for (let i = 0; i < steps.length; i++) {
|
|
103495
103756
|
const step = steps[i];
|
|
103496
|
-
|
|
103757
|
+
log16.log(` Step ${i + 1}/${steps.length}: ${step.name} (${step.type})`);
|
|
103497
103758
|
const stepResult = await this.executeStep(schedule, step, i);
|
|
103498
103759
|
stepResults.push(stepResult);
|
|
103499
103760
|
if (!stepResult.success) {
|
|
103500
103761
|
overallSuccess = false;
|
|
103501
103762
|
if (!step.continueOnFailure) {
|
|
103502
|
-
|
|
103763
|
+
log16.warn(` Step "${step.name}" failed \u2014 stopping execution`);
|
|
103503
103764
|
stoppedEarly = true;
|
|
103504
103765
|
break;
|
|
103505
103766
|
}
|
|
103506
|
-
|
|
103767
|
+
log16.warn(` Step "${step.name}" failed \u2014 continuing (continueOnFailure=true)`);
|
|
103507
103768
|
} else {
|
|
103508
|
-
|
|
103769
|
+
log16.log(` \u2713 Step "${step.name}" completed`);
|
|
103509
103770
|
}
|
|
103510
103771
|
}
|
|
103511
103772
|
const outputParts = [];
|
|
@@ -103518,7 +103779,7 @@ var init_cron_runner = __esm({
|
|
|
103518
103779
|
const failedSteps = stepResults.filter((sr) => !sr.success);
|
|
103519
103780
|
const error = failedSteps.length > 0 ? `${failedSteps.length} step(s) failed: ${failedSteps.map((s) => s.stepName).join(", ")}${stoppedEarly ? " (execution stopped)" : ""}` : void 0;
|
|
103520
103781
|
const status = overallSuccess ? "\u2713" : "\u2717";
|
|
103521
|
-
|
|
103782
|
+
log16.log(`${status} ${schedule.name}: ${stepResults.length}/${steps.length} steps executed, ${failedSteps.length} failed`);
|
|
103522
103783
|
return {
|
|
103523
103784
|
success: overallSuccess,
|
|
103524
103785
|
output,
|
|
@@ -103661,8 +103922,8 @@ var init_cron_runner = __esm({
|
|
|
103661
103922
|
const modelProvider = step.modelProvider?.trim() || defaultModel.provider;
|
|
103662
103923
|
const modelId = step.modelId?.trim() || defaultModel.modelId;
|
|
103663
103924
|
const model = modelProvider && modelId ? `${modelProvider}/${modelId}` : "default";
|
|
103664
|
-
|
|
103665
|
-
|
|
103925
|
+
log16.log(` AI prompt step "${step.name}" using model: ${model}`);
|
|
103926
|
+
log16.log(` Prompt: ${step.prompt.slice(0, 100)}${step.prompt.length > 100 ? "\u2026" : ""}`);
|
|
103666
103927
|
try {
|
|
103667
103928
|
const resultPromise = this.aiPromptExecutor(step.prompt, modelProvider, modelId);
|
|
103668
103929
|
const timeoutPromise = new Promise((_resolve, reject2) => {
|
|
@@ -103671,7 +103932,7 @@ var init_cron_runner = __esm({
|
|
|
103671
103932
|
const response = await Promise.race([resultPromise, timeoutPromise]);
|
|
103672
103933
|
const responseText = String(response ?? "");
|
|
103673
103934
|
const output = responseText.length > MAX_OUTPUT_LENGTH ? responseText.slice(0, MAX_OUTPUT_LENGTH) + "\n[output truncated]" : responseText;
|
|
103674
|
-
|
|
103935
|
+
log16.log(` \u2713 AI prompt step "${step.name}" completed (${responseText.length} chars)`);
|
|
103675
103936
|
return {
|
|
103676
103937
|
stepId: step.id,
|
|
103677
103938
|
stepName: step.name,
|
|
@@ -103683,7 +103944,7 @@ var init_cron_runner = __esm({
|
|
|
103683
103944
|
};
|
|
103684
103945
|
} catch (err) {
|
|
103685
103946
|
const errorMessage = err.message ?? String(err);
|
|
103686
|
-
|
|
103947
|
+
log16.warn(` \u2717 AI prompt step "${step.name}" failed: ${errorMessage}`);
|
|
103687
103948
|
return {
|
|
103688
103949
|
stepId: step.id,
|
|
103689
103950
|
stepName: step.name,
|
|
@@ -103727,7 +103988,7 @@ var init_cron_runner = __esm({
|
|
|
103727
103988
|
try {
|
|
103728
103989
|
const task = await this.store.createTask(taskInput);
|
|
103729
103990
|
const output = `Created task ${task.id}: ${task.title || task.description.slice(0, 80)}`;
|
|
103730
|
-
|
|
103991
|
+
log16.log(` \u2713 Create-task step "${step.name}" created task ${task.id}`);
|
|
103731
103992
|
return {
|
|
103732
103993
|
stepId: step.id,
|
|
103733
103994
|
stepName: step.name,
|
|
@@ -103739,7 +104000,7 @@ var init_cron_runner = __esm({
|
|
|
103739
104000
|
};
|
|
103740
104001
|
} catch (err) {
|
|
103741
104002
|
const errorMessage = err.message ?? String(err);
|
|
103742
|
-
|
|
104003
|
+
log16.warn(` \u2717 Create-task step "${step.name}" failed: ${errorMessage}`);
|
|
103743
104004
|
return {
|
|
103744
104005
|
stepId: step.id,
|
|
103745
104006
|
stepName: step.name,
|
|
@@ -103781,7 +104042,7 @@ function truncateOutput3(stdout, stderr) {
|
|
|
103781
104042
|
}
|
|
103782
104043
|
return output;
|
|
103783
104044
|
}
|
|
103784
|
-
var import_cron_parser4,
|
|
104045
|
+
var import_cron_parser4, log17, execAsync12, DEFAULT_TIMEOUT_MS9, MAX_BUFFER2, MAX_OUTPUT_LENGTH2, MAX_CATCH_UP_INTERVALS, RoutineRunner;
|
|
103785
104046
|
var init_routine_runner = __esm({
|
|
103786
104047
|
"../engine/src/routine-runner.ts"() {
|
|
103787
104048
|
"use strict";
|
|
@@ -103789,7 +104050,7 @@ var init_routine_runner = __esm({
|
|
|
103789
104050
|
init_cron_runner();
|
|
103790
104051
|
init_logger2();
|
|
103791
104052
|
init_shell_utils();
|
|
103792
|
-
|
|
104053
|
+
log17 = createLogger2("routine-runner");
|
|
103793
104054
|
execAsync12 = promisify17(exec14);
|
|
103794
104055
|
DEFAULT_TIMEOUT_MS9 = 5 * 60 * 1e3;
|
|
103795
104056
|
MAX_BUFFER2 = 1024 * 1024;
|
|
@@ -103826,7 +104087,7 @@ var init_routine_runner = __esm({
|
|
|
103826
104087
|
}
|
|
103827
104088
|
const concurrency = routine.executionPolicy ?? "queue";
|
|
103828
104089
|
if (concurrency === "reject" && this.inFlightExecutions.has(routineId)) {
|
|
103829
|
-
|
|
104090
|
+
log17.log(`Routine ${routineId} rejected \u2014 already running`);
|
|
103830
104091
|
return {
|
|
103831
104092
|
routineId,
|
|
103832
104093
|
success: false,
|
|
@@ -103837,7 +104098,7 @@ var init_routine_runner = __esm({
|
|
|
103837
104098
|
};
|
|
103838
104099
|
}
|
|
103839
104100
|
if (concurrency === "queue" && this.inFlightExecutions.has(routineId)) {
|
|
103840
|
-
|
|
104101
|
+
log17.log(`Routine ${routineId} queued \u2014 waiting for existing execution`);
|
|
103841
104102
|
const existingResult = await this.inFlightExecutions.get(routineId);
|
|
103842
104103
|
if (existingResult) {
|
|
103843
104104
|
await existingResult;
|
|
@@ -103885,7 +104146,7 @@ var init_routine_runner = __esm({
|
|
|
103885
104146
|
};
|
|
103886
104147
|
} catch (err) {
|
|
103887
104148
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
103888
|
-
|
|
104149
|
+
log17.error(`Routine ${routineId} execution failed: ${errorMessage}`);
|
|
103889
104150
|
try {
|
|
103890
104151
|
await this.options.routineStore.completeRoutineExecution(routineId, {
|
|
103891
104152
|
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -103893,7 +104154,7 @@ var init_routine_runner = __esm({
|
|
|
103893
104154
|
error: errorMessage
|
|
103894
104155
|
});
|
|
103895
104156
|
} catch (persistError) {
|
|
103896
|
-
|
|
104157
|
+
log17.error(`[${routineId}] Failed to persist error state: ${persistError}`);
|
|
103897
104158
|
}
|
|
103898
104159
|
return {
|
|
103899
104160
|
routineId,
|
|
@@ -104155,7 +104416,7 @@ var init_routine_runner = __esm({
|
|
|
104155
104416
|
if (missedIntervals.length === 0) {
|
|
104156
104417
|
return;
|
|
104157
104418
|
}
|
|
104158
|
-
|
|
104419
|
+
log17.log(`[${routine.id}] Running ${missedIntervals.length} catch-up executions`);
|
|
104159
104420
|
for (const missedInterval of missedIntervals) {
|
|
104160
104421
|
try {
|
|
104161
104422
|
await this.executeRoutine(routine.id, "cron", {
|
|
@@ -104163,11 +104424,11 @@ var init_routine_runner = __esm({
|
|
|
104163
104424
|
missedInterval: missedInterval.toISOString()
|
|
104164
104425
|
});
|
|
104165
104426
|
} catch (err) {
|
|
104166
|
-
|
|
104427
|
+
log17.error(`[${routine.id}] Catch-up execution failed: ${err}`);
|
|
104167
104428
|
}
|
|
104168
104429
|
}
|
|
104169
104430
|
} catch (err) {
|
|
104170
|
-
|
|
104431
|
+
log17.error(`[${routine.id}] Error calculating catch-up intervals: ${err}`);
|
|
104171
104432
|
}
|
|
104172
104433
|
}
|
|
104173
104434
|
/**
|
|
@@ -104753,91 +105014,6 @@ var init_stuck_task_detector = __esm({
|
|
|
104753
105014
|
}
|
|
104754
105015
|
});
|
|
104755
105016
|
|
|
104756
|
-
// ../engine/src/restart-recovery-coordinator.ts
|
|
104757
|
-
function hasStepProgress(task) {
|
|
104758
|
-
const steps = Array.isArray(task.steps) ? task.steps : [];
|
|
104759
|
-
return steps.some((step) => step.status === "done" || step.status === "in-progress" || step.status === "skipped");
|
|
104760
|
-
}
|
|
104761
|
-
function isNoTaskDoneFailure(task) {
|
|
104762
|
-
return task.status === "failed" && typeof task.error === "string" && task.error.toLowerCase().includes("without calling fn_task_done");
|
|
104763
|
-
}
|
|
104764
|
-
function findMissingWorktreeSessionPrefix(error) {
|
|
104765
|
-
for (const prefix of MISSING_WORKTREE_SESSION_PREFIXES) {
|
|
104766
|
-
if (error.includes(prefix)) {
|
|
104767
|
-
return prefix;
|
|
104768
|
-
}
|
|
104769
|
-
}
|
|
104770
|
-
return null;
|
|
104771
|
-
}
|
|
104772
|
-
function isMissingWorktreeSessionStartFailure(error) {
|
|
104773
|
-
if (typeof error !== "string") {
|
|
104774
|
-
return false;
|
|
104775
|
-
}
|
|
104776
|
-
return findMissingWorktreeSessionPrefix(error) !== null;
|
|
104777
|
-
}
|
|
104778
|
-
function extractMissingWorktreePathFromSessionStartFailure(error) {
|
|
104779
|
-
if (typeof error !== "string") return null;
|
|
104780
|
-
const prefix = findMissingWorktreeSessionPrefix(error);
|
|
104781
|
-
if (!prefix) return null;
|
|
104782
|
-
const idx = error.indexOf(prefix);
|
|
104783
|
-
const pathPart = error.slice(idx + prefix.length).trim();
|
|
104784
|
-
return pathPart.length > 0 ? pathPart : null;
|
|
104785
|
-
}
|
|
104786
|
-
function isRecoverableMissingWorktreeReviewFailure(task) {
|
|
104787
|
-
return task.column === "in-review" && !task.paused && task.status === "failed" && isMissingWorktreeSessionStartFailure(task.error) && hasStepProgress(task);
|
|
104788
|
-
}
|
|
104789
|
-
var log17, MISSING_WORKTREE_SESSION_PREFIXES, RestartRecoveryCoordinator;
|
|
104790
|
-
var init_restart_recovery_coordinator = __esm({
|
|
104791
|
-
"../engine/src/restart-recovery-coordinator.ts"() {
|
|
104792
|
-
"use strict";
|
|
104793
|
-
init_logger2();
|
|
104794
|
-
log17 = createLogger2("restart-recovery");
|
|
104795
|
-
MISSING_WORKTREE_SESSION_PREFIXES = [
|
|
104796
|
-
"Refusing to start coding agent in missing worktree:",
|
|
104797
|
-
"Refusing to start coding agent in incomplete worktree:",
|
|
104798
|
-
"Refusing to start coding agent in unregistered git worktree:"
|
|
104799
|
-
];
|
|
104800
|
-
RestartRecoveryCoordinator = class {
|
|
104801
|
-
constructor(store, executor) {
|
|
104802
|
-
this.store = store;
|
|
104803
|
-
this.executor = executor;
|
|
104804
|
-
}
|
|
104805
|
-
async recoverInterruptedRuns() {
|
|
104806
|
-
const allInProgress = await this.store.listTasks({ slim: true, column: "in-progress" });
|
|
104807
|
-
const candidates = allInProgress.filter((task) => task.column === "in-progress" && !task.paused);
|
|
104808
|
-
if (candidates.length === 0) return;
|
|
104809
|
-
let requeued = 0;
|
|
104810
|
-
for (const task of candidates) {
|
|
104811
|
-
if (!this.mustSafeRetry(task)) continue;
|
|
104812
|
-
await this.safeRequeue(task);
|
|
104813
|
-
requeued++;
|
|
104814
|
-
}
|
|
104815
|
-
if (requeued > 0) {
|
|
104816
|
-
log17.log(`Restart recovery requeued ${requeued} interrupted task(s) for safe retry`);
|
|
104817
|
-
}
|
|
104818
|
-
await this.executor.resumeOrphaned();
|
|
104819
|
-
}
|
|
104820
|
-
mustSafeRetry(task) {
|
|
104821
|
-
return isNoTaskDoneFailure(task) && !hasStepProgress(task);
|
|
104822
|
-
}
|
|
104823
|
-
async safeRequeue(task) {
|
|
104824
|
-
await this.store.updateTask(task.id, {
|
|
104825
|
-
status: "stuck-killed",
|
|
104826
|
-
worktree: null,
|
|
104827
|
-
branch: null,
|
|
104828
|
-
sessionFile: null,
|
|
104829
|
-
error: null
|
|
104830
|
-
});
|
|
104831
|
-
await this.store.logEntry(
|
|
104832
|
-
task.id,
|
|
104833
|
-
"Restart recovery: interrupted run had no step progress and no fn_task_done \u2014 requeued to todo for safe retry"
|
|
104834
|
-
);
|
|
104835
|
-
await this.store.moveTask(task.id, "todo");
|
|
104836
|
-
}
|
|
104837
|
-
};
|
|
104838
|
-
}
|
|
104839
|
-
});
|
|
104840
|
-
|
|
104841
105017
|
// ../engine/src/self-healing.ts
|
|
104842
105018
|
import { exec as exec15, execSync as execSync4 } from "node:child_process";
|
|
104843
105019
|
import { promisify as promisify18 } from "node:util";
|
|
@@ -110615,6 +110791,13 @@ var init_in_process_runtime = __esm({
|
|
|
110615
110791
|
getMessageStore() {
|
|
110616
110792
|
return this.messageStore;
|
|
110617
110793
|
}
|
|
110794
|
+
/**
|
|
110795
|
+
* Get the ChatStore instance (if initialized).
|
|
110796
|
+
* Returns undefined before start() or if initialization fails.
|
|
110797
|
+
*/
|
|
110798
|
+
getChatStore() {
|
|
110799
|
+
return this.chatStore;
|
|
110800
|
+
}
|
|
110618
110801
|
/**
|
|
110619
110802
|
* Get the project's Scheduler instance.
|
|
110620
110803
|
* @throws Error if runtime has not been started
|
|
@@ -113588,6 +113771,10 @@ ${detail}`
|
|
|
113588
113771
|
getMessageStore() {
|
|
113589
113772
|
return this.runtime.getMessageStore();
|
|
113590
113773
|
}
|
|
113774
|
+
/** Get the ChatStore (if initialized). Returns undefined before start(). */
|
|
113775
|
+
getChatStore() {
|
|
113776
|
+
return this.runtime.getChatStore();
|
|
113777
|
+
}
|
|
113591
113778
|
attachChatStore(chatStore) {
|
|
113592
113779
|
this.notificationService?.attachChatStore(chatStore);
|
|
113593
113780
|
}
|
|
@@ -119389,6 +119576,7 @@ PREFER structured question types over free-text. This makes the interview faster
|
|
|
119389
119576
|
- Features should be specific and actionable
|
|
119390
119577
|
- ALWAYS include verification/acceptance criteria at every level:
|
|
119391
119578
|
- Milestone: "verification" field \u2014 how to confirm this phase is complete (e.g., "All API endpoints return correct responses, integration tests pass")
|
|
119579
|
+
- Milestone: optional "acceptanceCriteria" field for explicit milestone-level completion bars (if omitted, acceptance can be auto-derived from child feature criteria/descriptions)
|
|
119392
119580
|
- Slice: "verification" field \u2014 how to confirm this work unit is done (e.g., "Auth flow works end-to-end from signup through login")
|
|
119393
119581
|
- Feature: "acceptanceCriteria" field \u2014 how to verify this specific deliverable (e.g., "JWT tokens expire after 1 hour and refresh correctly")
|
|
119394
119582
|
- Suggest sensible defaults and push for specificity
|
|
@@ -119411,7 +119599,7 @@ For text question (use ONLY for names, URLs, or truly unique free-form input):
|
|
|
119411
119599
|
{"type": "question", "data": {"id": "q-project-name", "type": "text", "question": "What is the project or product name?", "description": "Used in documentation and configuration"}}
|
|
119412
119600
|
|
|
119413
119601
|
For completion (when you have enough information):
|
|
119414
|
-
{"type": "complete", "data": {"missionTitle": "Refined mission title", "missionDescription": "Comprehensive mission description based on the conversation", "milestones": [{"title": "Milestone title", "description": "What this phase achieves", "verification": "How to confirm this milestone is complete", "slices": [{"title": "Slice title", "description": "What this work unit covers", "verification": "How to confirm this slice is done", "features": [{"title": "Feature title", "description": "What to build", "acceptanceCriteria": "How to verify this feature works"}]}]}]}}`;
|
|
119602
|
+
{"type": "complete", "data": {"missionTitle": "Refined mission title", "missionDescription": "Comprehensive mission description based on the conversation", "milestones": [{"title": "Milestone title", "description": "What this phase achieves", "verification": "How to confirm this milestone is complete", "acceptanceCriteria": "Optional explicit milestone criteria (omit to auto-derive from features)", "slices": [{"title": "Slice title", "description": "What this work unit covers", "verification": "How to confirm this slice is done", "features": [{"title": "Feature title", "description": "What to build", "acceptanceCriteria": "How to verify this feature works"}]}]}]}}`;
|
|
119415
119603
|
sessions3 = /* @__PURE__ */ new Map();
|
|
119416
119604
|
rateLimits3 = /* @__PURE__ */ new Map();
|
|
119417
119605
|
cleanupInterval4 = setInterval(cleanupExpiredSessions3, CLEANUP_INTERVAL_MS4);
|
|
@@ -130592,9 +130780,13 @@ function cacheKeyForStore(store) {
|
|
|
130592
130780
|
}
|
|
130593
130781
|
function getOrCreateScopedChatStore(store, fallbackChatStore) {
|
|
130594
130782
|
const key = cacheKeyForStore(store);
|
|
130783
|
+
if (fallbackChatStore) {
|
|
130784
|
+
scopedChatStoreCache.set(key, fallbackChatStore);
|
|
130785
|
+
return fallbackChatStore;
|
|
130786
|
+
}
|
|
130595
130787
|
const cached = scopedChatStoreCache.get(key);
|
|
130596
130788
|
if (cached) return cached;
|
|
130597
|
-
const chatStore =
|
|
130789
|
+
const chatStore = new ChatStore(store.getFusionDir(), store.getDatabase());
|
|
130598
130790
|
scopedChatStoreCache.set(key, chatStore);
|
|
130599
130791
|
return chatStore;
|
|
130600
130792
|
}
|
|
@@ -130609,9 +130801,10 @@ async function resolveProjectChatContext(options) {
|
|
|
130609
130801
|
const engine = engineManager?.getEngine(projectId);
|
|
130610
130802
|
try {
|
|
130611
130803
|
const scopedStore = engine?.getTaskStore() ?? await getOrCreateProjectStore(projectId);
|
|
130804
|
+
const engineChatStore = engine?.getChatStore?.();
|
|
130612
130805
|
return {
|
|
130613
130806
|
store: scopedStore,
|
|
130614
|
-
chatStore: getOrCreateScopedChatStore(scopedStore)
|
|
130807
|
+
chatStore: getOrCreateScopedChatStore(scopedStore, engineChatStore)
|
|
130615
130808
|
};
|
|
130616
130809
|
} catch {
|
|
130617
130810
|
return {
|
|
@@ -130844,14 +131037,17 @@ function registerChatRoomRoutes(ctx) {
|
|
|
130844
131037
|
const { chatStore } = await resolveRoomScopedServices(req, getRequestedProjectId(req));
|
|
130845
131038
|
const room = chatStore.getRoom(roomId);
|
|
130846
131039
|
if (!room) throw notFound(`Chat room ${roomId} not found`);
|
|
130847
|
-
const { limit: limitStr, offset: offsetStr, before } = req.query;
|
|
131040
|
+
const { limit: limitStr, offset: offsetStr, before, order: orderStr } = req.query;
|
|
130848
131041
|
const limit = limitStr !== void 0 ? parseInt(String(limitStr), 10) : 50;
|
|
130849
131042
|
const offset = offsetStr !== void 0 ? parseInt(String(offsetStr), 10) : 0;
|
|
131043
|
+
const order = orderStr === void 0 ? "asc" : String(orderStr);
|
|
130850
131044
|
if (!Number.isFinite(limit) || limit < 1) throw badRequest("limit must be a positive integer");
|
|
130851
131045
|
if (!Number.isFinite(offset) || offset < 0) throw badRequest("offset must be a non-negative integer");
|
|
131046
|
+
if (order !== "asc" && order !== "desc") throw badRequest("order must be either asc or desc");
|
|
130852
131047
|
const messages = chatStore.getRoomMessages(roomId, {
|
|
130853
131048
|
limit,
|
|
130854
131049
|
offset,
|
|
131050
|
+
order,
|
|
130855
131051
|
...before ? { before } : {}
|
|
130856
131052
|
});
|
|
130857
131053
|
res.json({ messages });
|
|
@@ -181307,7 +181503,7 @@ var init_register_auth_routes = __esm({
|
|
|
181307
181503
|
if (!origin || isLocalhostOrigin(origin)) {
|
|
181308
181504
|
return false;
|
|
181309
181505
|
}
|
|
181310
|
-
if (providerId === "openai-codex" || providerId === "anthropic") {
|
|
181506
|
+
if (providerId === "openai-codex" || providerId === "anthropic" || providerId === "github-copilot") {
|
|
181311
181507
|
return false;
|
|
181312
181508
|
}
|
|
181313
181509
|
return true;
|
|
@@ -183080,7 +183276,8 @@ function createMissionRouter(store, missionAutopilot, aiSessionStore, missionExe
|
|
|
183080
183276
|
const milestone = missionStore.addMilestone(mission.id, {
|
|
183081
183277
|
title: milestoneData.title,
|
|
183082
183278
|
description: milestoneData.description || void 0,
|
|
183083
|
-
verification: milestoneData.verification
|
|
183279
|
+
verification: milestoneData.verification,
|
|
183280
|
+
acceptanceCriteria: milestoneData.acceptanceCriteria
|
|
183084
183281
|
});
|
|
183085
183282
|
const milestoneAssertionText = milestoneData.verification || milestoneData.description || `Verify milestone completion: ${milestoneData.title}`;
|
|
183086
183283
|
missionStore.addContractAssertion(milestone.id, {
|
|
@@ -183115,6 +183312,7 @@ function createMissionRouter(store, missionAutopilot, aiSessionStore, missionExe
|
|
|
183115
183312
|
missionStore.linkFeatureToAssertion(feature.id, assertion.id);
|
|
183116
183313
|
}
|
|
183117
183314
|
}
|
|
183315
|
+
missionStore.applyDerivedMilestoneAcceptanceCriteria(milestone.id);
|
|
183118
183316
|
}
|
|
183119
183317
|
cleanupMissionInterviewSession2(sessionId);
|
|
183120
183318
|
const result = missionStore.getMissionWithHierarchy(mission.id);
|
|
@@ -183321,7 +183519,7 @@ function createMissionRouter(store, missionAutopilot, aiSessionStore, missionExe
|
|
|
183321
183519
|
"/:missionId/milestones",
|
|
183322
183520
|
catchTypedHandler(async (req, res) => {
|
|
183323
183521
|
const { missionId } = req.params;
|
|
183324
|
-
const { title, description, dependencies } = req.body;
|
|
183522
|
+
const { title, description, dependencies, acceptanceCriteria } = req.body;
|
|
183325
183523
|
if (!validateMissionId(missionId)) {
|
|
183326
183524
|
throw badRequest("Invalid mission ID format");
|
|
183327
183525
|
}
|
|
@@ -183332,10 +183530,12 @@ function createMissionRouter(store, missionAutopilot, aiSessionStore, missionExe
|
|
|
183332
183530
|
const validatedTitle = validateTitle(title);
|
|
183333
183531
|
const validatedDescription = validateDescription2(description);
|
|
183334
183532
|
const validatedDependencies = validateStringArray(dependencies, "dependencies");
|
|
183533
|
+
const validatedAcceptanceCriteria = validateDescription2(acceptanceCriteria);
|
|
183335
183534
|
const input = {
|
|
183336
183535
|
title: validatedTitle,
|
|
183337
183536
|
description: validatedDescription,
|
|
183338
|
-
dependencies: validatedDependencies
|
|
183537
|
+
dependencies: validatedDependencies,
|
|
183538
|
+
acceptanceCriteria: validatedAcceptanceCriteria
|
|
183339
183539
|
};
|
|
183340
183540
|
const milestone = missionStore.addMilestone(missionId, input);
|
|
183341
183541
|
res.status(201).json(milestone);
|
|
@@ -183384,7 +183584,7 @@ function createMissionRouter(store, missionAutopilot, aiSessionStore, missionExe
|
|
|
183384
183584
|
"/milestones/:milestoneId",
|
|
183385
183585
|
catchTypedHandler(async (req, res) => {
|
|
183386
183586
|
const { milestoneId } = req.params;
|
|
183387
|
-
const { title, description, status, dependencies } = req.body;
|
|
183587
|
+
const { title, description, status, dependencies, acceptanceCriteria } = req.body;
|
|
183388
183588
|
if (!validateMilestoneId(milestoneId)) {
|
|
183389
183589
|
throw badRequest("Invalid milestone ID format");
|
|
183390
183590
|
}
|
|
@@ -183401,6 +183601,9 @@ function createMissionRouter(store, missionAutopilot, aiSessionStore, missionExe
|
|
|
183401
183601
|
if (dependencies !== void 0) {
|
|
183402
183602
|
updates.dependencies = validateStringArray(dependencies, "dependencies");
|
|
183403
183603
|
}
|
|
183604
|
+
if (acceptanceCriteria !== void 0) {
|
|
183605
|
+
updates.acceptanceCriteria = validateDescription2(acceptanceCriteria);
|
|
183606
|
+
}
|
|
183404
183607
|
if (Object.keys(updates).length === 0) {
|
|
183405
183608
|
throw badRequest("No valid fields to update");
|
|
183406
183609
|
}
|
|
@@ -198903,7 +199106,7 @@ function createServer(store, options) {
|
|
|
198903
199106
|
if (engineManager) {
|
|
198904
199107
|
const engine2 = engineManager.getEngine(projectId);
|
|
198905
199108
|
scopedStore = engine2?.getTaskStore() ?? await getOrCreateProjectStore(projectId);
|
|
198906
|
-
scopedChatStore = getOrCreateScopedChatStore(scopedStore);
|
|
199109
|
+
scopedChatStore = getOrCreateScopedChatStore(scopedStore, engine2?.getChatStore?.());
|
|
198907
199110
|
agentStore = engine2?.getAgentStore();
|
|
198908
199111
|
messageStore = engine2?.getMessageStore();
|
|
198909
199112
|
automationStore = engine2?.getAutomationStore();
|