@seawork/server 1.0.20 → 1.0.21-rc.2
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/server/client/daemon-client.d.ts +7 -1
- package/dist/server/client/daemon-client.d.ts.map +1 -1
- package/dist/server/client/daemon-client.js +26 -0
- package/dist/server/client/daemon-client.js.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +9 -3
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.js +83 -5
- package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
- package/dist/server/server/bootstrap.d.ts.map +1 -1
- package/dist/server/server/bootstrap.js +4 -0
- package/dist/server/server/bootstrap.js.map +1 -1
- package/dist/server/server/pr-review-watcher.d.ts +24 -0
- package/dist/server/server/pr-review-watcher.d.ts.map +1 -0
- package/dist/server/server/pr-review-watcher.js +281 -0
- package/dist/server/server/pr-review-watcher.js.map +1 -0
- package/dist/server/server/session.d.ts +4 -0
- package/dist/server/server/session.d.ts.map +1 -1
- package/dist/server/server/session.js +117 -1
- package/dist/server/server/session.js.map +1 -1
- package/dist/server/server/workspace-ignore/store.d.ts +17 -0
- package/dist/server/server/workspace-ignore/store.d.ts.map +1 -0
- package/dist/server/server/workspace-ignore/store.js +92 -0
- package/dist/server/server/workspace-ignore/store.js.map +1 -0
- package/dist/server/shared/messages.d.ts +584 -0
- package/dist/server/shared/messages.d.ts.map +1 -1
- package/dist/server/shared/messages.js +49 -0
- package/dist/server/shared/messages.js.map +1 -1
- package/dist/server/utils/directory-suggestions.d.ts +2 -0
- package/dist/server/utils/directory-suggestions.d.ts.map +1 -1
- package/dist/server/utils/directory-suggestions.js +81 -11
- package/dist/server/utils/directory-suggestions.js.map +1 -1
- package/package.json +3 -3
|
@@ -20,6 +20,12 @@ import { buildCodexFeatures, codexModelSupportsFastMode } from "./codex-feature-
|
|
|
20
20
|
import { formatDiagnosticStatus, formatProviderDiagnostic, formatProviderDiagnosticError, resolveBinaryVersion, toDiagnosticErrorMessage, } from "./diagnostic-utils.js";
|
|
21
21
|
const DEFAULT_TIMEOUT_MS = 14 * 24 * 60 * 60 * 1000;
|
|
22
22
|
const TURN_START_TIMEOUT_MS = 90 * 1000;
|
|
23
|
+
// issue #259: window after turn/interrupt during which the next turn/start
|
|
24
|
+
// gets a sentinel reminder prepended to its input.
|
|
25
|
+
const CANCEL_REMINDER_WINDOW_MS = 60 * 1000;
|
|
26
|
+
const CANCEL_REMINDER_TEXT = "[seawork system note] The user canceled the previous turn before it finished. " +
|
|
27
|
+
"Ignore any prior user message that did not receive a complete answer. " +
|
|
28
|
+
"Only respond to the message below.";
|
|
23
29
|
const CODEX_EXTERNAL_MIGRATION_MIN_VERSION = "0.128.0";
|
|
24
30
|
const CODEX_PROVIDER = "codex";
|
|
25
31
|
const CODEX_SEAWORK_PROVIDER_ID = "seawork";
|
|
@@ -2092,6 +2098,10 @@ class CodexAppServerAgentSession {
|
|
|
2092
2098
|
this.capabilities = CODEX_APP_SERVER_CAPABILITIES;
|
|
2093
2099
|
this.currentThreadId = null;
|
|
2094
2100
|
this.currentTurnId = null;
|
|
2101
|
+
// issue #259: codex thread is append-only and turn/interrupt does not retract
|
|
2102
|
+
// the canceled user message. Track when interrupt completed so the next
|
|
2103
|
+
// turn/start can warn the model to ignore the prior prompt.
|
|
2104
|
+
this.lastCanceledAt = null;
|
|
2095
2105
|
this.client = null;
|
|
2096
2106
|
this.subscribers = new Set();
|
|
2097
2107
|
this.nextTurnOrdinal = 0;
|
|
@@ -2280,8 +2290,29 @@ class CodexAppServerAgentSession {
|
|
|
2280
2290
|
}
|
|
2281
2291
|
this.planModeEnabled = value;
|
|
2282
2292
|
this.refreshResolvedCollaborationMode();
|
|
2293
|
+
// collaborationMode switches may bring in a different settings.model
|
|
2294
|
+
// (when config.model is unset and plan/code modes carry different model
|
|
2295
|
+
// hints). Mirror setModel()'s fast-tier guard so we don't ship
|
|
2296
|
+
// serviceTier:"fast" to a model that doesn't support it.
|
|
2297
|
+
if (this.serviceTier && !codexModelSupportsFastMode(this.effectiveTurnModel())) {
|
|
2298
|
+
// Clear both runtime and persisted state — the constructor restores
|
|
2299
|
+
// serviceTier from config.featureValues.fast_mode on session reload,
|
|
2300
|
+
// so leaving the persisted flag true would bring the invalid state back.
|
|
2301
|
+
this.serviceTier = null;
|
|
2302
|
+
this.config.featureValues = {
|
|
2303
|
+
...(this.config.featureValues ?? {}),
|
|
2304
|
+
fast_mode: false,
|
|
2305
|
+
};
|
|
2306
|
+
}
|
|
2283
2307
|
this.cachedRuntimeInfo = null;
|
|
2284
2308
|
}
|
|
2309
|
+
effectiveTurnModel() {
|
|
2310
|
+
const collabModel = this.resolvedCollaborationMode?.settings?.model;
|
|
2311
|
+
if (typeof collabModel === "string" && collabModel.length > 0) {
|
|
2312
|
+
return collabModel;
|
|
2313
|
+
}
|
|
2314
|
+
return this.config.model;
|
|
2315
|
+
}
|
|
2285
2316
|
rememberPlanResult(item) {
|
|
2286
2317
|
if (item.detail.type !== "plan") {
|
|
2287
2318
|
return;
|
|
@@ -2317,14 +2348,16 @@ class CodexAppServerAgentSession {
|
|
|
2317
2348
|
this.emitEvent({ type: "permission_requested", provider: CODEX_PROVIDER, request });
|
|
2318
2349
|
}
|
|
2319
2350
|
/**
|
|
2320
|
-
* Prepare the session for plan implementation by disabling plan
|
|
2321
|
-
*
|
|
2322
|
-
*
|
|
2351
|
+
* Prepare the session for plan implementation by disabling plan mode (so the
|
|
2352
|
+
* next turn runs in `code` collaboration mode instead of generating another
|
|
2353
|
+
* plan) and returning the implementation prompt. fast_mode is intentionally
|
|
2354
|
+
* preserved — it only controls the inference tier and is orthogonal to the
|
|
2355
|
+
* plan→implement transition. The caller is responsible for starting the
|
|
2356
|
+
* turn through the normal streamAgent path.
|
|
2323
2357
|
*/
|
|
2324
2358
|
preparePlanImplementation(params) {
|
|
2325
2359
|
const planText = typeof params.planText === "string" ? normalizePlanMarkdown(params.planText) : "";
|
|
2326
2360
|
this.applyFeatureValue("plan_mode", false);
|
|
2327
|
-
this.applyFeatureValue("fast_mode", false);
|
|
2328
2361
|
return buildCodexPlanImplementationPrompt(planText);
|
|
2329
2362
|
}
|
|
2330
2363
|
registerRequestHandlers() {
|
|
@@ -2558,7 +2591,7 @@ class CodexAppServerAgentSession {
|
|
|
2558
2591
|
else {
|
|
2559
2592
|
await this.ensureThread();
|
|
2560
2593
|
}
|
|
2561
|
-
const input = await this.buildUserInput(effectivePrompt);
|
|
2594
|
+
const input = this.maybePrependCancelReminder(await this.buildUserInput(effectivePrompt));
|
|
2562
2595
|
const preset = MODE_PRESETS[this.currentMode] ?? MODE_PRESETS[DEFAULT_CODEX_MODE_ID];
|
|
2563
2596
|
const approvalPolicy = this.config.approvalPolicy ?? preset.approvalPolicy;
|
|
2564
2597
|
const sandboxPolicyType = this.config.sandboxMode ?? preset.sandbox;
|
|
@@ -2612,6 +2645,11 @@ class CodexAppServerAgentSession {
|
|
|
2612
2645
|
throw error;
|
|
2613
2646
|
}
|
|
2614
2647
|
this.logger.info({ turnId, elapsedMs: Date.now() - turnStartT0 }, "[timing] codex turn/start acknowledged");
|
|
2648
|
+
// issue #259: do NOT consume `lastCanceledAt` here. The sentinel is only
|
|
2649
|
+
// cleared once we see turn/completed with status "completed" — a
|
|
2650
|
+
// turn/start ack can still race with turn/completed: failed, in which
|
|
2651
|
+
// case the orphaned prompt is still in the thread and the next retry
|
|
2652
|
+
// must carry the reminder (PR #273 review #3).
|
|
2615
2653
|
return { turnId };
|
|
2616
2654
|
}
|
|
2617
2655
|
subscribe(callback) {
|
|
@@ -2835,6 +2873,12 @@ class CodexAppServerAgentSession {
|
|
|
2835
2873
|
threadId: this.currentThreadId,
|
|
2836
2874
|
turnId: this.currentTurnId,
|
|
2837
2875
|
});
|
|
2876
|
+
// issue #259: codex protocol has no thread-truncate RPC, so a canceled
|
|
2877
|
+
// user prompt stays in the thread. The sentinel timestamp is set on the
|
|
2878
|
+
// turn/completed `interrupted` event below (not here), because a
|
|
2879
|
+
// successful turn/interrupt RPC can race with normal completion — if
|
|
2880
|
+
// the model already finished, the prompt has a paired assistant reply
|
|
2881
|
+
// and the next turn must NOT prepend the sentinel.
|
|
2838
2882
|
}
|
|
2839
2883
|
catch (error) {
|
|
2840
2884
|
this.logger.warn({ error }, "Failed to interrupt Codex turn");
|
|
@@ -2948,6 +2992,27 @@ class CodexAppServerAgentSession {
|
|
|
2948
2992
|
const blocks = prompt;
|
|
2949
2993
|
return await codexAppServerTurnInputFromPrompt(blocks, this.logger);
|
|
2950
2994
|
}
|
|
2995
|
+
// issue #259: if the previous turn was canceled via turn/interrupt and the
|
|
2996
|
+
// user resends a prompt within the window, prepend a one-shot sentinel so
|
|
2997
|
+
// the model knows to ignore the orphaned user message that codex's
|
|
2998
|
+
// append-only thread cannot retract. Sentinel lifecycle is owned by
|
|
2999
|
+
// turn/completed: it is only cleared on a "completed" status. We do NOT
|
|
3000
|
+
// clear it here on a successful turn/start ack, because that turn could
|
|
3001
|
+
// still end as turn/completed: failed, in which case the orphaned prompt
|
|
3002
|
+
// remains in the thread and the next retry must still carry the reminder
|
|
3003
|
+
// (PR #273 review #3). The only side-effect here is eagerly dropping a
|
|
3004
|
+
// stale timestamp that fell outside the window — no turn/completed is
|
|
3005
|
+
// expected to arrive for it.
|
|
3006
|
+
maybePrependCancelReminder(input) {
|
|
3007
|
+
if (this.lastCanceledAt === null)
|
|
3008
|
+
return input;
|
|
3009
|
+
const elapsed = Date.now() - this.lastCanceledAt;
|
|
3010
|
+
if (elapsed >= CANCEL_REMINDER_WINDOW_MS) {
|
|
3011
|
+
this.lastCanceledAt = null;
|
|
3012
|
+
return input;
|
|
3013
|
+
}
|
|
3014
|
+
return [{ type: "text", text: CANCEL_REMINDER_TEXT }, ...input];
|
|
3015
|
+
}
|
|
2951
3016
|
emitEvent(event) {
|
|
2952
3017
|
if (event.type === "timeline") {
|
|
2953
3018
|
if (event.item.type === "assistant_message") {
|
|
@@ -3004,9 +3069,22 @@ class CodexAppServerAgentSession {
|
|
|
3004
3069
|
});
|
|
3005
3070
|
}
|
|
3006
3071
|
else if (parsed.status === "interrupted") {
|
|
3072
|
+
// issue #259: the user prompt for this turn is now orphaned in the
|
|
3073
|
+
// append-only thread. Record the moment so the next turn/start can
|
|
3074
|
+
// prepend a sentinel telling the model to ignore it.
|
|
3075
|
+
this.lastCanceledAt = Date.now();
|
|
3007
3076
|
this.emitEvent({ type: "turn_canceled", provider: CODEX_PROVIDER, reason: "interrupted" });
|
|
3008
3077
|
}
|
|
3009
3078
|
else {
|
|
3079
|
+
// issue #259: a normal `completed` turn means the prior user prompt
|
|
3080
|
+
// has a paired assistant reply — clear any pending sentinel (e.g.
|
|
3081
|
+
// from a turn/interrupt that raced with normal completion) so we
|
|
3082
|
+
// don't tell the model to ignore valid history. Unknown future
|
|
3083
|
+
// statuses leave `lastCanceledAt` untouched: we don't know whether
|
|
3084
|
+
// the prompt was answered or orphaned, so we keep current state.
|
|
3085
|
+
if (parsed.status === "completed") {
|
|
3086
|
+
this.lastCanceledAt = null;
|
|
3087
|
+
}
|
|
3010
3088
|
if (this.planModeEnabled && this.latestPlanResult?.text) {
|
|
3011
3089
|
this.emitSyntheticPlanApprovalRequest(this.latestPlanResult.text);
|
|
3012
3090
|
}
|