@gajae-code/coding-agent 0.7.0 → 0.7.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/CHANGELOG.md +28 -0
- package/dist/types/cli/notify-cli.d.ts +2 -0
- package/dist/types/config/settings-schema.d.ts +39 -2
- package/dist/types/extensibility/shared-events.d.ts +1 -0
- package/dist/types/gjc-runtime/launch-tmux.d.ts +1 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +1 -1
- package/dist/types/gjc-runtime/tmux-common.d.ts +3 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +2 -0
- package/dist/types/lsp/types.d.ts +2 -0
- package/dist/types/notifications/attachment-registry.d.ts +17 -0
- package/dist/types/notifications/chat-adapters.d.ts +9 -0
- package/dist/types/notifications/config.d.ts +9 -1
- package/dist/types/notifications/engine.d.ts +59 -0
- package/dist/types/notifications/managed-daemon.d.ts +48 -0
- package/dist/types/notifications/telegram-daemon.d.ts +19 -0
- package/dist/types/notifications/threaded-inbound.d.ts +19 -0
- package/dist/types/notifications/threaded-render.d.ts +6 -1
- package/dist/types/session/agent-session.d.ts +2 -0
- package/dist/types/tools/fetch.d.ts +23 -0
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/telegram-send.d.ts +32 -0
- package/dist/types/web/insane/bridge.d.ts +103 -0
- package/dist/types/web/insane/url-guard.d.ts +22 -0
- package/dist/types/web/search/provider.d.ts +18 -1
- package/dist/types/web/search/providers/insane.d.ts +53 -0
- package/dist/types/web/search/providers/text-citations.d.ts +23 -0
- package/dist/types/web/search/types.d.ts +12 -4
- package/package.json +10 -8
- package/scripts/verify-insane-vendor.ts +132 -0
- package/src/cli/args.ts +1 -1
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/notify-cli.ts +152 -5
- package/src/cli.ts +1 -3
- package/src/commands/team.ts +1 -1
- package/src/config/settings-schema.ts +30 -1
- package/src/defaults/gjc/skills/ralplan/SKILL.md +11 -4
- package/src/edit/modes/replace.ts +1 -1
- package/src/extensibility/shared-events.ts +1 -0
- package/src/gjc-runtime/launch-tmux.ts +27 -5
- package/src/gjc-runtime/ledger-event-renderer.ts +1 -0
- package/src/gjc-runtime/ralplan-runtime.ts +2 -2
- package/src/gjc-runtime/tmux-common.ts +8 -0
- package/src/gjc-runtime/tmux-sessions.ts +8 -1
- package/src/gjc-runtime/workflow-manifest.generated.json +29 -0
- package/src/gjc-runtime/workflow-manifest.ts +7 -2
- package/src/hashline/hash.ts +1 -1
- package/src/internal-urls/docs-index.generated.ts +9 -8
- package/src/lsp/config.ts +16 -3
- package/src/lsp/defaults.json +7 -0
- package/src/lsp/types.ts +2 -0
- package/src/modes/controllers/event-controller.ts +15 -0
- package/src/modes/interactive-mode.ts +46 -2
- package/src/modes/utils/context-usage.ts +2 -2
- package/src/notifications/attachment-registry.ts +23 -0
- package/src/notifications/chat-adapters.ts +147 -0
- package/src/notifications/config.ts +23 -2
- package/src/notifications/engine.ts +100 -0
- package/src/notifications/index.ts +224 -45
- package/src/notifications/managed-daemon.ts +163 -0
- package/src/notifications/telegram-daemon.ts +235 -14
- package/src/notifications/threaded-inbound.ts +60 -4
- package/src/notifications/threaded-render.ts +20 -2
- package/src/session/agent-session.ts +82 -51
- package/src/tools/ask.ts +3 -2
- package/src/tools/fetch.ts +78 -1
- package/src/tools/index.ts +3 -0
- package/src/tools/telegram-send.ts +137 -0
- package/src/web/insane/bridge.ts +350 -0
- package/src/web/insane/url-guard.ts +155 -0
- package/src/web/search/provider.ts +77 -18
- package/src/web/search/providers/anthropic.ts +70 -3
- package/src/web/search/providers/codex.ts +1 -119
- package/src/web/search/providers/gemini.ts +99 -0
- package/src/web/search/providers/insane.ts +551 -0
- package/src/web/search/providers/openai-compatible.ts +66 -32
- package/src/web/search/providers/text-citations.ts +111 -0
- package/src/web/search/types.ts +13 -2
- package/vendor/insane-search/LICENSE +21 -0
- package/vendor/insane-search/MANIFEST.json +24 -0
- package/vendor/insane-search/engine/__init__.py +23 -0
- package/vendor/insane-search/engine/__main__.py +128 -0
- package/vendor/insane-search/engine/bias_check.py +183 -0
- package/vendor/insane-search/engine/executor.py +254 -0
- package/vendor/insane-search/engine/fetch_chain.py +725 -0
- package/vendor/insane-search/engine/learning.py +175 -0
- package/vendor/insane-search/engine/phase0.py +214 -0
- package/vendor/insane-search/engine/safety.py +91 -0
- package/vendor/insane-search/engine/templates/package.json +11 -0
- package/vendor/insane-search/engine/templates/playwright_mobile_chrome.js +188 -0
- package/vendor/insane-search/engine/templates/playwright_real_chrome.js +243 -0
- package/vendor/insane-search/engine/tests/test_hardening.py +57 -0
- package/vendor/insane-search/engine/tests/test_smoke.py +152 -0
- package/vendor/insane-search/engine/tests/test_u1.py +200 -0
- package/vendor/insane-search/engine/tests/test_u4.py +131 -0
- package/vendor/insane-search/engine/tests/test_u5.py +163 -0
- package/vendor/insane-search/engine/tests/test_u7.py +124 -0
- package/vendor/insane-search/engine/transport.py +211 -0
- package/vendor/insane-search/engine/url_transforms.py +98 -0
- package/vendor/insane-search/engine/validators.py +331 -0
- package/vendor/insane-search/engine/waf_detector.py +214 -0
- package/vendor/insane-search/engine/waf_profiles.yaml +162 -0
package/src/cli/notify-cli.ts
CHANGED
|
@@ -29,6 +29,8 @@ export interface NotifyCommandDeps {
|
|
|
29
29
|
pollIntervalMs?: number;
|
|
30
30
|
setupChatId?: string;
|
|
31
31
|
setupRedact?: boolean;
|
|
32
|
+
setupInteractive?: boolean;
|
|
33
|
+
threadedModePrompt?: (message: string) => Promise<string>;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
interface TelegramApiResponse<T> {
|
|
@@ -47,6 +49,18 @@ interface TelegramUpdate {
|
|
|
47
49
|
};
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
interface TelegramUser {
|
|
53
|
+
id: number;
|
|
54
|
+
is_bot?: boolean;
|
|
55
|
+
first_name?: string;
|
|
56
|
+
username?: string;
|
|
57
|
+
has_topics_enabled?: boolean;
|
|
58
|
+
allows_users_to_create_topics?: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type ThreadedModeState = "enabled" | "disabled" | "unknown";
|
|
62
|
+
type ThreadedModeFinalLabel = "verified" | "unverified" | "unknown";
|
|
63
|
+
|
|
50
64
|
const DEFAULT_API_BASE = "https://api.telegram.org";
|
|
51
65
|
const DEFAULT_POLL_TIMEOUT_MS = 60_000;
|
|
52
66
|
const DEFAULT_POLL_INTERVAL_MS = 1_000;
|
|
@@ -120,7 +134,11 @@ async function runSetup(deps: NotifyCommandDeps): Promise<void> {
|
|
|
120
134
|
throw new Error("Telegram bot token is required.");
|
|
121
135
|
}
|
|
122
136
|
|
|
123
|
-
await
|
|
137
|
+
const user = await getMe(fetchImpl, apiBase, token);
|
|
138
|
+
const threadedState = await verifyThreadedMode(fetchImpl, apiBase, token, user, {
|
|
139
|
+
interactive: resolveSetupInteractive(deps),
|
|
140
|
+
prompt: deps.threadedModePrompt ?? promptForThreadedMode,
|
|
141
|
+
});
|
|
124
142
|
process.stdout.write(
|
|
125
143
|
"Token validated. Message your bot now from the private Telegram chat to pair notifications.\n",
|
|
126
144
|
);
|
|
@@ -145,7 +163,9 @@ async function runSetup(deps: NotifyCommandDeps): Promise<void> {
|
|
|
145
163
|
if (deps.setupRedact) settings.set("notifications.redact", true);
|
|
146
164
|
await settings.flush();
|
|
147
165
|
|
|
148
|
-
process.stdout.write(
|
|
166
|
+
process.stdout.write(
|
|
167
|
+
`Notifications enabled. botToken=${maskToken(token)} chatId=${chatId} threaded=${threadedLabel(threadedState)}\n`,
|
|
168
|
+
);
|
|
149
169
|
}
|
|
150
170
|
|
|
151
171
|
async function promptForToken(): Promise<string> {
|
|
@@ -160,14 +180,135 @@ async function promptForToken(): Promise<string> {
|
|
|
160
180
|
}
|
|
161
181
|
}
|
|
162
182
|
|
|
183
|
+
const THREADED_ENABLED_SUCCESS =
|
|
184
|
+
"Telegram Threaded Mode capability verified for this bot. GJC will request a private-chat topic per session; if Telegram ever refuses topic creation, notifications fall back to this flat chat with a one-time nudge.\n";
|
|
185
|
+
|
|
186
|
+
const THREADED_MISSING_WARNING =
|
|
187
|
+
"Warning: Telegram getMe did not include has_topics_enabled, so GJC cannot verify private-chat Threaded Mode capability for this bot. Setup will continue; update Telegram/Bot API support or re-run setup if per-session topics fail.\n";
|
|
188
|
+
|
|
189
|
+
const THREADED_NONINTERACTIVE_WARNING =
|
|
190
|
+
"Warning: Telegram Threaded Mode capability is OFF for this bot. Setup will be saved because this run is non-interactive, but per-session Telegram delivery may fail closed until the bot owner enables Threaded Mode in @BotFather. GJC cannot enable it through the Bot API.\n";
|
|
191
|
+
|
|
192
|
+
const THREADED_DISABLED_GUIDANCE =
|
|
193
|
+
"Telegram Threaded Mode is OFF for this bot. GJC needs Telegram private-chat topics so each session can use its own thread.\n" +
|
|
194
|
+
"GJC cannot enable this through the Bot API. Open @BotFather, select this bot, enable Threaded Mode / forum topics for private chats, then return here.\n" +
|
|
195
|
+
"Telegram may require an additional Stars purchase fee for private-chat topics.\n";
|
|
196
|
+
|
|
197
|
+
const THREADED_DISABLED_PROMPT =
|
|
198
|
+
"Press Enter after enabling Threaded Mode, or type skip to finish setup with a warning: ";
|
|
199
|
+
|
|
200
|
+
const THREADED_STILL_OFF = "Telegram still reports Threaded Mode OFF for this bot.\n";
|
|
201
|
+
|
|
202
|
+
const THREADED_RETRY_PROMPT = "Press Enter to check again, or type skip to finish setup with a warning: ";
|
|
203
|
+
|
|
204
|
+
const THREADED_SKIP_WARNING =
|
|
205
|
+
"Warning: continuing without verified Telegram Threaded Mode capability. Setup will be saved, but per-session Telegram delivery may fail closed until Threaded Mode is enabled in BotFather.\n";
|
|
206
|
+
|
|
207
|
+
const THREADED_INVALID_INPUT = "Type Enter to retry or skip to continue with a warning.\n";
|
|
208
|
+
|
|
209
|
+
const THREADED_RETRY_INPUTS = new Set(["", "y", "yes", "r", "retry"]);
|
|
210
|
+
const THREADED_SKIP_INPUTS = new Set(["s", "skip", "n", "no"]);
|
|
211
|
+
|
|
212
|
+
function isTelegramUser(value: unknown): value is TelegramUser {
|
|
213
|
+
return Boolean(value) && typeof value === "object" && typeof (value as { id?: unknown }).id === "number";
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function getMe(fetchImpl: typeof fetch, apiBase: string, token: string): Promise<TelegramUser> {
|
|
217
|
+
const user = await callTelegram<unknown>(fetchImpl, apiBase, token, "getMe", {});
|
|
218
|
+
if (!isTelegramUser(user)) {
|
|
219
|
+
throw new Error("Telegram getMe returned invalid Telegram response: missing valid User result.");
|
|
220
|
+
}
|
|
221
|
+
return user;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function threadedModeState(user: TelegramUser): ThreadedModeState {
|
|
225
|
+
if (user.has_topics_enabled === true) return "enabled";
|
|
226
|
+
if (user.has_topics_enabled === false) return "disabled";
|
|
227
|
+
return "unknown";
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function threadedLabel(state: ThreadedModeState): ThreadedModeFinalLabel {
|
|
231
|
+
if (state === "enabled") return "verified";
|
|
232
|
+
if (state === "disabled") return "unverified";
|
|
233
|
+
return "unknown";
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function resolveSetupInteractive(deps: NotifyCommandDeps): boolean {
|
|
237
|
+
if (deps.setupInteractive !== undefined) return deps.setupInteractive;
|
|
238
|
+
return Boolean(process.stdin.isTTY) && !deps.setupChatId?.trim();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async function promptForThreadedMode(message: string): Promise<string> {
|
|
242
|
+
if (!process.stdin.isTTY) return "skip";
|
|
243
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
244
|
+
try {
|
|
245
|
+
return (await rl.question(message)).trim();
|
|
246
|
+
} finally {
|
|
247
|
+
rl.close();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async function verifyThreadedMode(
|
|
252
|
+
fetchImpl: typeof fetch,
|
|
253
|
+
apiBase: string,
|
|
254
|
+
token: string,
|
|
255
|
+
initialUser: TelegramUser,
|
|
256
|
+
opts: { interactive: boolean; prompt: (message: string) => Promise<string> },
|
|
257
|
+
): Promise<ThreadedModeState> {
|
|
258
|
+
const classify = (user: TelegramUser): ThreadedModeState | undefined => {
|
|
259
|
+
const state = threadedModeState(user);
|
|
260
|
+
if (state === "enabled") {
|
|
261
|
+
process.stdout.write(THREADED_ENABLED_SUCCESS);
|
|
262
|
+
return "enabled";
|
|
263
|
+
}
|
|
264
|
+
if (state === "unknown") {
|
|
265
|
+
process.stdout.write(THREADED_MISSING_WARNING);
|
|
266
|
+
return "unknown";
|
|
267
|
+
}
|
|
268
|
+
return undefined;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const initial = classify(initialUser);
|
|
272
|
+
if (initial) return initial;
|
|
273
|
+
|
|
274
|
+
if (!opts.interactive) {
|
|
275
|
+
process.stdout.write(THREADED_NONINTERACTIVE_WARNING);
|
|
276
|
+
return "disabled";
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
process.stdout.write(THREADED_DISABLED_GUIDANCE);
|
|
280
|
+
let firstPrompt = true;
|
|
281
|
+
for (;;) {
|
|
282
|
+
const answer = (await opts.prompt(firstPrompt ? THREADED_DISABLED_PROMPT : THREADED_RETRY_PROMPT))
|
|
283
|
+
.trim()
|
|
284
|
+
.toLowerCase();
|
|
285
|
+
firstPrompt = false;
|
|
286
|
+
if (THREADED_SKIP_INPUTS.has(answer)) {
|
|
287
|
+
process.stdout.write(THREADED_SKIP_WARNING);
|
|
288
|
+
return "disabled";
|
|
289
|
+
}
|
|
290
|
+
if (!THREADED_RETRY_INPUTS.has(answer)) {
|
|
291
|
+
process.stdout.write(THREADED_INVALID_INPUT);
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
const resolved = classify(await getMe(fetchImpl, apiBase, token));
|
|
295
|
+
if (resolved) return resolved;
|
|
296
|
+
process.stdout.write(THREADED_STILL_OFF);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
163
300
|
async function runStatus(deps: NotifyCommandDeps): Promise<void> {
|
|
164
301
|
const settings = await getSettings(deps);
|
|
165
302
|
const cfg = getNotificationConfig(settings);
|
|
166
303
|
process.stdout.write(
|
|
167
304
|
`${chalk.bold("Notifications")}\n` +
|
|
168
305
|
` enabled: ${cfg.enabled}\n` +
|
|
169
|
-
` botToken: ${maskToken(cfg.botToken)}\n` +
|
|
170
|
-
` chatId: ${cfg.chatId ?? "(unset)"}\n` +
|
|
306
|
+
` telegram.botToken: ${maskToken(cfg.botToken)}\n` +
|
|
307
|
+
` telegram.chatId: ${cfg.chatId ?? "(unset)"}\n` +
|
|
308
|
+
` discord.botToken: ${maskToken(cfg.discord.botToken)}\n` +
|
|
309
|
+
` discord.channelId: ${cfg.discord.channelId ?? "(unset)"}\n` +
|
|
310
|
+
` slack.botToken: ${maskToken(cfg.slack.botToken)}\n` +
|
|
311
|
+
` slack.channelId: ${cfg.slack.channelId ?? "(unset)"}\n` +
|
|
171
312
|
` redact: ${cfg.redact}\n`,
|
|
172
313
|
);
|
|
173
314
|
}
|
|
@@ -263,12 +404,18 @@ ${chalk.bold("Usage:")}
|
|
|
263
404
|
${APP_NAME} notify status
|
|
264
405
|
|
|
265
406
|
${chalk.bold("Subcommands:")}
|
|
266
|
-
setup Pair a Telegram bot token with a private chat
|
|
407
|
+
setup Pair a Telegram bot token with a private chat and verify Threaded Mode capability
|
|
267
408
|
status Show notification configuration without secrets
|
|
268
409
|
|
|
269
410
|
${chalk.bold("Examples:")}
|
|
270
411
|
${APP_NAME} notify setup
|
|
271
412
|
${APP_NAME} notify setup --token <botToken> --chat-id <chatId> [--redact]
|
|
272
413
|
${APP_NAME} notify status
|
|
414
|
+
|
|
415
|
+
${chalk.bold("Threaded Mode:")}
|
|
416
|
+
GJC uses Telegram private-chat topics for per-session threads. Setup verifies the bot
|
|
417
|
+
capability via getMe.has_topics_enabled. If it is off, enable Threaded Mode in @BotFather;
|
|
418
|
+
bots cannot toggle it through the Bot API. If Telegram refuses topic creation at runtime,
|
|
419
|
+
GJC delivers flat to the paired private chat and nudges you to enable Threaded Mode.
|
|
273
420
|
`);
|
|
274
421
|
}
|
package/src/cli.ts
CHANGED
|
@@ -176,9 +176,7 @@ async function runSmokeTest(): Promise<void> {
|
|
|
176
176
|
// the COMPILED single binary (dev runs only load the on-disk .node). Loading the
|
|
177
177
|
// natives module triggers loadNative()/embedded extraction; calling each new
|
|
178
178
|
// export confirms the symbols are present in the shipped binary.
|
|
179
|
-
const { h06FormatHashLines, h02ScoreSequenceFuzzy, h01FindBestFuzzyMatch } = await import(
|
|
180
|
-
"../../natives/native/index.js"
|
|
181
|
-
);
|
|
179
|
+
const { h06FormatHashLines, h02ScoreSequenceFuzzy, h01FindBestFuzzyMatch } = await import("@gajae-code/natives");
|
|
182
180
|
const hashed = h06FormatHashLines("a\nb", 1);
|
|
183
181
|
if (hashed.split("\n").length !== 2) {
|
|
184
182
|
throw new Error(`smoke-test: h06FormatHashLines returned unexpected output: ${JSON.stringify(hashed)}`);
|
package/src/commands/team.ts
CHANGED
|
@@ -95,7 +95,7 @@ export default class Team extends Command {
|
|
|
95
95
|
};
|
|
96
96
|
|
|
97
97
|
static examples = [
|
|
98
|
-
"gjc --tmux # start
|
|
98
|
+
"gjc --tmux # start the required tmux-backed leader session first",
|
|
99
99
|
'gjc team 3:executor "Implement the approved plan"',
|
|
100
100
|
"gjc team status <team-name> --json",
|
|
101
101
|
"gjc team monitor <team-name> --json",
|
|
@@ -254,10 +254,14 @@ export const SETTINGS_SCHEMA = {
|
|
|
254
254
|
"auth.broker.url": { type: "string", default: undefined },
|
|
255
255
|
"auth.broker.token": { type: "string", default: undefined },
|
|
256
256
|
|
|
257
|
-
// Notifications (Telegram
|
|
257
|
+
// Notifications (shared daemon with Telegram/Discord/Slack presentation adapters)
|
|
258
258
|
"notifications.enabled": { type: "boolean", default: false },
|
|
259
259
|
"notifications.telegram.botToken": { type: "string", default: undefined },
|
|
260
260
|
"notifications.telegram.chatId": { type: "string", default: undefined },
|
|
261
|
+
"notifications.discord.botToken": { type: "string", default: undefined },
|
|
262
|
+
"notifications.discord.channelId": { type: "string", default: undefined },
|
|
263
|
+
"notifications.slack.botToken": { type: "string", default: undefined },
|
|
264
|
+
"notifications.slack.channelId": { type: "string", default: undefined },
|
|
261
265
|
"notifications.redact": { type: "boolean", default: false },
|
|
262
266
|
"notifications.verbosity": {
|
|
263
267
|
type: "string",
|
|
@@ -2104,6 +2108,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
2104
2108
|
ui: { tab: "tools", label: "Read URLs", description: "Allow the read tool to fetch and process URLs" },
|
|
2105
2109
|
},
|
|
2106
2110
|
|
|
2111
|
+
"web.insaneFallback": {
|
|
2112
|
+
type: "boolean",
|
|
2113
|
+
default: false,
|
|
2114
|
+
ui: {
|
|
2115
|
+
tab: "tools",
|
|
2116
|
+
label: "Insane Search Fallback",
|
|
2117
|
+
description:
|
|
2118
|
+
"Opt in to the vendored insane-search escalation for blocked public URL reads (403/WAF/JS-gated). Off by default. Requires preinstalled python3 + curl_cffi (and node + playwright/stealth for the browser phase); changes network posture by enabling TLS/browser impersonation for public pages.",
|
|
2119
|
+
},
|
|
2120
|
+
},
|
|
2121
|
+
|
|
2107
2122
|
"github.enabled": {
|
|
2108
2123
|
type: "boolean",
|
|
2109
2124
|
default: false,
|
|
@@ -2731,6 +2746,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
2731
2746
|
values: [
|
|
2732
2747
|
"auto",
|
|
2733
2748
|
"duckduckgo",
|
|
2749
|
+
"insane",
|
|
2734
2750
|
"exa",
|
|
2735
2751
|
"brave",
|
|
2736
2752
|
"jina",
|
|
@@ -2763,6 +2779,11 @@ export const SETTINGS_SCHEMA = {
|
|
|
2763
2779
|
label: "DuckDuckGo",
|
|
2764
2780
|
description: "Keyless default — no API key or OAuth required",
|
|
2765
2781
|
},
|
|
2782
|
+
{
|
|
2783
|
+
value: "insane",
|
|
2784
|
+
label: "Insane",
|
|
2785
|
+
description: "Keyless safe public-route fallback inspired by upstream insane-search",
|
|
2786
|
+
},
|
|
2766
2787
|
{ value: "exa", label: "Exa", description: "Uses Exa API when EXA_API_KEY is set" },
|
|
2767
2788
|
{ value: "brave", label: "Brave", description: "Requires BRAVE_API_KEY" },
|
|
2768
2789
|
{ value: "jina", label: "Jina", description: "Requires JINA_API_KEY" },
|
|
@@ -3177,6 +3198,14 @@ export interface NotificationsSettings {
|
|
|
3177
3198
|
botToken: string | undefined;
|
|
3178
3199
|
chatId: string | undefined;
|
|
3179
3200
|
};
|
|
3201
|
+
discord: {
|
|
3202
|
+
botToken: string | undefined;
|
|
3203
|
+
channelId: string | undefined;
|
|
3204
|
+
};
|
|
3205
|
+
slack: {
|
|
3206
|
+
botToken: string | undefined;
|
|
3207
|
+
channelId: string | undefined;
|
|
3208
|
+
};
|
|
3180
3209
|
redact: boolean;
|
|
3181
3210
|
daemon: {
|
|
3182
3211
|
idleTimeoutMs: number;
|
|
@@ -47,7 +47,7 @@ Planning artifacts and stage handoffs MUST be persisted through the ralplan CLI
|
|
|
47
47
|
gjc ralplan --write --stage <type> --stage_n <N> --artifact "markdown file path or markdown string"
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
Use stage values that match the producer or artifact kind, such as `planner`, `architect`, `critic`, `revision`, `adr`, or `final`. Increment `--stage_n` for each consensus-loop pass. The `--artifact` value may be either a markdown file path prepared outside `.gjc/` for ingestion or the markdown content string itself. The native `--write` handler persists markdown under `.gjc/_session-{sessionid}/plans/ralplan/<run-id>/stage-<NN>-<stage>.md`, maintains an `index.jsonl` audit log, and for `final` stages additionally writes a `pending-approval.md` copy. Direct `write`, `edit`, or `ast_edit` calls against `.gjc/_session-{sessionid}/specs`, `.gjc/_session-{sessionid}/plans`, `.gjc/_session-{sessionid}/state`, or any other `.gjc/` path are forbidden unless an explicit force override is active.
|
|
50
|
+
Use stage values that match the producer or artifact kind, such as `planner`, `architect`, `critic`, `revision`, `post-interview`, `adr`, or `final`. Increment `--stage_n` for each consensus-loop pass. The `--artifact` value may be either a markdown file path prepared outside `.gjc/` for ingestion or the markdown content string itself. The native `--write` handler persists markdown under `.gjc/_session-{sessionid}/plans/ralplan/<run-id>/stage-<NN>-<stage>.md`, maintains an `index.jsonl` audit log, and for `final` stages additionally writes a `pending-approval.md` copy. Direct `write`, `edit`, or `ast_edit` calls against `.gjc/_session-{sessionid}/specs`, `.gjc/_session-{sessionid}/plans`, `.gjc/_session-{sessionid}/state`, or any other `.gjc/` path are forbidden unless an explicit force override is active.
|
|
51
51
|
|
|
52
52
|
While ralplan is active it is a pre-approval planning phase: product-code mutation tools (`write`/`edit`/`ast_edit`) and product-mutating `bash` (e.g. `tee src/...`, redirects into the project tree) are blocked, exactly like deep-interview. Prefer passing the `--artifact` markdown **inline** (the content string) so no scratch file is needed; this is mandatory for restricted role agents (see below). Only the leader, and only when an artifact is too large to pass inline, may stage it as a file in a system temp directory (`os.tmpdir()`/`$TMPDIR`, `/tmp`, `/var/tmp`) outside the project tree and pass that path — never write scratch files into the repo or `.gjc/`. Product code is mutated only after the plan is approved and execution begins.
|
|
53
53
|
|
|
@@ -80,9 +80,16 @@ The consensus workflow:
|
|
|
80
80
|
d. Return to Critic evaluation
|
|
81
81
|
e. Repeat this loop until Critic returns `APPROVE` or 5 iterations are reached
|
|
82
82
|
f. If 5 iterations are reached without `APPROVE`, present the best version to the user
|
|
83
|
-
6.
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
6. **Post-ralplan interview** (intent reconciliation gate): After Critic returns `APPROVE` and before the plan is finalized, reconcile the consensus plan against the user's actual intent. The goal is to make sure ralplan did not silently bake in assumptions that conflict with what the user wants.
|
|
84
|
+
a. **Collect open items** from the run: every assumption the Planner/Architect/Critic resolved by assumption rather than by stated fact, every ambiguity flagged during review, and every decision the loop made without explicit user input. Source these from the persisted `planner`/`architect`/`critic`/`revision` stage artifacts, not from memory.
|
|
85
|
+
b. **Cross-check prior context for conflicts**: glob `.gjc/_session-{sessionid}/specs/deep-interview-*.md` and other prior specs/plans/context relevant by topic. For each, list points where the consensus plan contradicts, weakens, or expands beyond a previously crystallized decision, constraint, or non-goal. Cite the conflicting artifact and line/section.
|
|
86
|
+
c. **Reconcile with the user**:
|
|
87
|
+
- *(--interactive only)* Use the `ask` tool to confirm the open assumptions and conflicts **one at a time**, weakest/highest-impact first, polishing intent. If any confirmation reveals that the plan diverges from user intent, route the consolidated correction back into the re-review loop (step 5b Planner revision) and re-run Architect + Critic before returning here. Cap at the same 5-iteration ceiling.
|
|
88
|
+
- *(automated mode)* Do not ask. Embed every unconfirmed assumption and every detected prior-context conflict into the final plan under an **## Intent Reconciliation** section as explicit open confirmations the user must review at the `pending approval` gate, so nothing is silently assumed.
|
|
89
|
+
d. Persist the reconciliation with `gjc ralplan --write --stage post-interview --stage_n <N> --artifact "..." --json`, then return the receipt/path plus a compact status (reconciled-clean / reconciled-with-revision / open-confirmations-pending) instead of pasting the full body.
|
|
90
|
+
7. On reconciliation completion, mark the plan `pending approval` unless explicit execution approval has already been captured, persist the ADR/final plan via `gjc ralplan --write --stage final --stage_n <N> --artifact "..."`, and do not directly edit `.gjc/_session-{sessionid}/plans`. *(--interactive only)* If `--interactive` is set, use the `ask` tool to present the plan with approval options (Approve execution via ultragoal (Recommended) / Approve execution via team (only when tmux-based interactive worker parallelization is required) / Compact then return for execution approval / Request changes / Reject). Final plan must include ADR (Decision, Drivers, Alternatives considered, Why chosen, Consequences, Follow-ups) and, when present, the **## Intent Reconciliation** section. Otherwise, output the final plan and stop before any mutation or delegation.
|
|
91
|
+
8. *(--interactive only)* User chooses: Approve ultragoal execution (recommended), Approve team execution (tmux parallelization only), Request changes, or Reject
|
|
92
|
+
9. *(--interactive only)* On approval: invoke `/skill:ultragoal` for execution by default; invoke `/skill:team` only when the user explicitly needs tmux-based interactive worker parallelization -- never implement directly
|
|
86
93
|
|
|
87
94
|
Before invoking `/skill:team` or `/skill:ultragoal`, mark ralplan ready for handoff so the skill tool's chain guard permits the transition:
|
|
88
95
|
|
|
@@ -42,7 +42,7 @@ let scoreSequenceFuzzyNative:
|
|
|
42
42
|
let findBestFuzzyMatchNative:
|
|
43
43
|
| ((content: string, target: string, threshold: number) => NativeBestFuzzyMatchResult)
|
|
44
44
|
| undefined;
|
|
45
|
-
void import("
|
|
45
|
+
void import("@gajae-code/natives")
|
|
46
46
|
.then(mod => {
|
|
47
47
|
if (typeof mod.h02ScoreSequenceFuzzy === "function") {
|
|
48
48
|
scoreSequenceFuzzyNative = mod.h02ScoreSequenceFuzzy;
|
|
@@ -217,6 +217,7 @@ export interface AutoCompactionEndEvent {
|
|
|
217
217
|
errorMessage?: string;
|
|
218
218
|
/** True when compaction was skipped for a benign reason (no model, no candidates, nothing to compact). */
|
|
219
219
|
skipped?: boolean;
|
|
220
|
+
continuationSkipReason?: "auto_continue_disabled_non_resumable_tail";
|
|
220
221
|
}
|
|
221
222
|
|
|
222
223
|
/** Fired when auto-retry starts */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Buffer } from "node:buffer";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { safeStderrWrite } from "@gajae-code/utils";
|
|
4
|
+
import { VERSION } from "@gajae-code/utils/dirs";
|
|
4
5
|
import type { Args } from "../cli/args";
|
|
5
6
|
import { tmuxRuntimeSessionPath } from "./session-layout";
|
|
6
7
|
import { GJC_COORDINATOR_SESSION_ID_ENV, GJC_COORDINATOR_SESSION_STATE_FILE_ENV } from "./session-state-sidecar";
|
|
@@ -16,7 +17,7 @@ import {
|
|
|
16
17
|
type GjcTmuxProfileCommand,
|
|
17
18
|
resolveGjcTmuxCommand,
|
|
18
19
|
} from "./tmux-common";
|
|
19
|
-
import { findGjcTmuxSessionByName, findGjcTmuxSessionByScope } from "./tmux-sessions";
|
|
20
|
+
import { findGjcTmuxSessionByName, findGjcTmuxSessionByScope, type GjcTmuxSessionStatus } from "./tmux-sessions";
|
|
20
21
|
|
|
21
22
|
export {
|
|
22
23
|
buildGjcTmuxProfileCommands,
|
|
@@ -88,6 +89,13 @@ export interface TmuxLaunchPlan {
|
|
|
88
89
|
function explicitTmuxSessionName(env: NodeJS.ProcessEnv): string | undefined {
|
|
89
90
|
return env.GJC_TMUX_SESSION?.trim() || undefined;
|
|
90
91
|
}
|
|
92
|
+
function hasCurrentGjcVersion(session: GjcTmuxSessionStatus | undefined): boolean {
|
|
93
|
+
return session?.version === VERSION;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function allowsExistingTmuxAttach(parsed: Args, env: NodeJS.ProcessEnv): boolean {
|
|
97
|
+
return Boolean(parsed.continue || parsed.resume || explicitTmuxSessionName(env));
|
|
98
|
+
}
|
|
91
99
|
|
|
92
100
|
function findExistingSessionForLaunch(context: {
|
|
93
101
|
env: NodeJS.ProcessEnv;
|
|
@@ -96,7 +104,8 @@ function findExistingSessionForLaunch(context: {
|
|
|
96
104
|
}): string | undefined {
|
|
97
105
|
const explicit = explicitTmuxSessionName(context.env);
|
|
98
106
|
if (explicit) return findGjcTmuxSessionByName(explicit, context.env)?.name;
|
|
99
|
-
|
|
107
|
+
const scoped = findGjcTmuxSessionByScope(context.project, context.branch, context.env);
|
|
108
|
+
return hasCurrentGjcVersion(scoped) ? scoped?.name : undefined;
|
|
100
109
|
}
|
|
101
110
|
|
|
102
111
|
export interface GjcTmuxProfileResult {
|
|
@@ -116,6 +125,7 @@ export interface GjcTmuxProfileContext {
|
|
|
116
125
|
project?: string | null;
|
|
117
126
|
sessionId?: string | null;
|
|
118
127
|
sessionStateFile?: string | null;
|
|
128
|
+
version?: string | null;
|
|
119
129
|
}
|
|
120
130
|
|
|
121
131
|
interface CommandResolutionContext {
|
|
@@ -195,6 +205,7 @@ export function applyGjcTmuxProfile(context: GjcTmuxProfileContext): GjcTmuxProf
|
|
|
195
205
|
project: context.project ?? null,
|
|
196
206
|
sessionId: context.sessionId ?? env[GJC_COORDINATOR_SESSION_ID_ENV] ?? null,
|
|
197
207
|
sessionStateFile: context.sessionStateFile ?? env[GJC_COORDINATOR_SESSION_STATE_FILE_ENV] ?? null,
|
|
208
|
+
version: context.version ?? null,
|
|
198
209
|
});
|
|
199
210
|
if (commands.length === 0) return { skipped: true, commands: [], failures: [] };
|
|
200
211
|
const spawnSync = context.spawnSync ?? defaultSpawnSync;
|
|
@@ -345,6 +356,11 @@ function readCurrentBranch(cwd: string): string | null {
|
|
|
345
356
|
function cleanupCreatedTmuxSession(plan: TmuxLaunchPlan, spawnSync: TmuxSpawnSync, options: TmuxSpawnOptions): void {
|
|
346
357
|
spawnSync(plan.tmuxCommand, ["kill-session", "-t", `=${plan.sessionName}`], options);
|
|
347
358
|
}
|
|
359
|
+
function isTmuxAttachDisconnectError(result: TmuxSpawnResult): boolean {
|
|
360
|
+
if (result.signalCode === "SIGHUP") return true;
|
|
361
|
+
const stderr = result.stderr?.toLowerCase() ?? "";
|
|
362
|
+
return stderr.includes("eio") || stderr.includes("input/output error");
|
|
363
|
+
}
|
|
348
364
|
|
|
349
365
|
export function buildDefaultTmuxLaunchPlan(context: TmuxLaunchContext): TmuxLaunchPlan | undefined {
|
|
350
366
|
const env = context.env ?? process.env;
|
|
@@ -370,14 +386,15 @@ export function buildDefaultTmuxLaunchPlan(context: TmuxLaunchContext): TmuxLaun
|
|
|
370
386
|
tmuxRuntimeSessionPath(cwd, gjcSessionId, buildGjcTmuxSessionSlug(sessionName));
|
|
371
387
|
const tmuxAvailable = context.tmuxAvailable ?? Bun.which(tmuxCommand) !== null;
|
|
372
388
|
if (!tmuxAvailable) return undefined;
|
|
373
|
-
const existingSessionName =
|
|
374
|
-
"existingBranchSessionName" in context
|
|
389
|
+
const existingSessionName = allowsExistingTmuxAttach(context.parsed, env)
|
|
390
|
+
? "existingBranchSessionName" in context
|
|
375
391
|
? (context.existingBranchSessionName ?? undefined)
|
|
376
392
|
: findExistingSessionForLaunch({
|
|
377
393
|
env,
|
|
378
394
|
project,
|
|
379
395
|
branch,
|
|
380
|
-
})
|
|
396
|
+
})
|
|
397
|
+
: undefined;
|
|
381
398
|
const innerCommand = buildInnerCommand(
|
|
382
399
|
{
|
|
383
400
|
cwd,
|
|
@@ -457,6 +474,7 @@ export function launchDefaultTmuxIfNeeded(context: TmuxLaunchContext): boolean {
|
|
|
457
474
|
project: plan.project,
|
|
458
475
|
sessionId: plan.sessionId ?? null,
|
|
459
476
|
sessionStateFile: plan.sessionStateFile ?? null,
|
|
477
|
+
version: VERSION,
|
|
460
478
|
});
|
|
461
479
|
const ownershipFailure = profile.failures.find(item => item.command.args.includes("@gjc-profile"));
|
|
462
480
|
if (ownershipFailure) {
|
|
@@ -470,6 +488,10 @@ export function launchDefaultTmuxIfNeeded(context: TmuxLaunchContext): boolean {
|
|
|
470
488
|
if (created.exitCode !== 0) return false;
|
|
471
489
|
const attached = spawnSync(plan.tmuxCommand, ["attach-session", "-t", `=${plan.sessionName}`], options);
|
|
472
490
|
if (attached.exitCode === 0) return true;
|
|
491
|
+
if (isTmuxAttachDisconnectError(attached)) {
|
|
492
|
+
(context.diagnosticWriter ?? safeStderrWrite)(formatTmuxLaunchDiagnostic("attach disconnected", attached.stderr));
|
|
493
|
+
return true;
|
|
494
|
+
}
|
|
473
495
|
cleanupCreatedTmuxSession(plan, spawnSync, options);
|
|
474
496
|
(context.diagnosticWriter ?? safeStderrWrite)(formatTmuxLaunchDiagnostic("attach failed", attached.stderr));
|
|
475
497
|
return true;
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
*
|
|
38
38
|
* 2. **Artifact write**: `gjc ralplan --write --stage <type> --stage_n <N> --artifact
|
|
39
39
|
* <path-or-string> [--run-id <id>] [--session-id <id>] [--json]` persists Planner / Architect
|
|
40
|
-
* / Critic / revision / ADR / final markdown under `.gjc/plans/ralplan/<run-id>/`, maintains
|
|
40
|
+
* / Critic / revision / post-interview / ADR / final markdown under `.gjc/plans/ralplan/<run-id>/`, maintains
|
|
41
41
|
* an `index.jsonl` audit log, copies `final` stages to `pending-approval.md`, and advances
|
|
42
42
|
* the HUD chip to reflect the latest persisted stage.
|
|
43
43
|
*/
|
|
@@ -48,7 +48,7 @@ export interface RalplanCommandResult {
|
|
|
48
48
|
stderr?: string;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
const KNOWN_STAGES = ["planner", "architect", "critic", "revision", "adr", "final"] as const;
|
|
51
|
+
const KNOWN_STAGES = ["planner", "architect", "critic", "revision", "post-interview", "adr", "final"] as const;
|
|
52
52
|
type RalplanStage = (typeof KNOWN_STAGES)[number];
|
|
53
53
|
|
|
54
54
|
const KNOWN_ARCHITECT_KINDS = new Set(["openai-code"]);
|
|
@@ -10,6 +10,7 @@ export const GJC_TMUX_BRANCH_SLUG_OPTION = "@gjc-branch-slug";
|
|
|
10
10
|
export const GJC_TMUX_PROJECT_OPTION = "@gjc-project";
|
|
11
11
|
export const GJC_TMUX_SESSION_ID_OPTION = "@gjc-session-id";
|
|
12
12
|
export const GJC_TMUX_SESSION_STATE_FILE_OPTION = "@gjc-session-state-file";
|
|
13
|
+
export const GJC_TMUX_VERSION_OPTION = "@gjc-version";
|
|
13
14
|
|
|
14
15
|
export interface GjcTmuxProfileCommand {
|
|
15
16
|
description: string;
|
|
@@ -101,6 +102,7 @@ export function buildGjcTmuxRequiredProfileCommands(
|
|
|
101
102
|
project?: string | null;
|
|
102
103
|
sessionId?: string | null;
|
|
103
104
|
sessionStateFile?: string | null;
|
|
105
|
+
version?: string | null;
|
|
104
106
|
} = {},
|
|
105
107
|
): GjcTmuxProfileCommand[] {
|
|
106
108
|
const commands: GjcTmuxProfileCommand[] = [
|
|
@@ -134,6 +136,11 @@ export function buildGjcTmuxRequiredProfileCommands(
|
|
|
134
136
|
description: "record GJC session state marker",
|
|
135
137
|
args: ["set-option", "-t", target, GJC_TMUX_SESSION_STATE_FILE_OPTION, metadata.sessionStateFile],
|
|
136
138
|
});
|
|
139
|
+
if (metadata.version)
|
|
140
|
+
commands.push({
|
|
141
|
+
description: "record GJC version identity",
|
|
142
|
+
args: ["set-option", "-t", target, GJC_TMUX_VERSION_OPTION, metadata.version],
|
|
143
|
+
});
|
|
137
144
|
return commands;
|
|
138
145
|
}
|
|
139
146
|
|
|
@@ -146,6 +153,7 @@ export function buildGjcTmuxProfileCommands(
|
|
|
146
153
|
project?: string | null;
|
|
147
154
|
sessionId?: string | null;
|
|
148
155
|
sessionStateFile?: string | null;
|
|
156
|
+
version?: string | null;
|
|
149
157
|
} = {},
|
|
150
158
|
): GjcTmuxProfileCommand[] {
|
|
151
159
|
const commands = buildGjcTmuxRequiredProfileCommands(target, metadata);
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
GJC_TMUX_PROJECT_OPTION,
|
|
11
11
|
GJC_TMUX_SESSION_ID_OPTION,
|
|
12
12
|
GJC_TMUX_SESSION_STATE_FILE_OPTION,
|
|
13
|
+
GJC_TMUX_VERSION_OPTION,
|
|
13
14
|
normalizeTmuxCreatedAt,
|
|
14
15
|
resolveGjcTmuxCommand,
|
|
15
16
|
} from "./tmux-common";
|
|
@@ -26,6 +27,7 @@ export interface GjcTmuxSessionStatus {
|
|
|
26
27
|
project?: string;
|
|
27
28
|
sessionId?: string;
|
|
28
29
|
sessionStateFile?: string;
|
|
30
|
+
version?: string;
|
|
29
31
|
panePids: number[];
|
|
30
32
|
profile?: string;
|
|
31
33
|
}
|
|
@@ -37,6 +39,7 @@ export interface GjcTmuxSessionTagsForGc {
|
|
|
37
39
|
branchSlug?: string;
|
|
38
40
|
sessionId?: string;
|
|
39
41
|
sessionStateFile?: string;
|
|
42
|
+
version?: string;
|
|
40
43
|
createdAt?: string;
|
|
41
44
|
attached?: boolean;
|
|
42
45
|
panePids?: number[];
|
|
@@ -86,6 +89,7 @@ function parseSessionLine(line: string): GjcTmuxSessionStatus | null {
|
|
|
86
89
|
project = "",
|
|
87
90
|
sessionId = "",
|
|
88
91
|
sessionStateFile = "",
|
|
92
|
+
version = "",
|
|
89
93
|
] = line.split("\t");
|
|
90
94
|
if (!name) return null;
|
|
91
95
|
return {
|
|
@@ -105,6 +109,7 @@ function parseSessionLine(line: string): GjcTmuxSessionStatus | null {
|
|
|
105
109
|
profile: profile || undefined,
|
|
106
110
|
sessionId: sessionId || undefined,
|
|
107
111
|
sessionStateFile: sessionStateFile || undefined,
|
|
112
|
+
version: version || undefined,
|
|
108
113
|
};
|
|
109
114
|
}
|
|
110
115
|
|
|
@@ -131,7 +136,7 @@ function runListSessions(format: string, env: NodeJS.ProcessEnv = process.env):
|
|
|
131
136
|
|
|
132
137
|
function listSessionLines(env: NodeJS.ProcessEnv = process.env): string[] {
|
|
133
138
|
return runListSessions(
|
|
134
|
-
`#{session_name}\t#{session_windows}\t#{session_attached}\t#{session_created}\t#{${GJC_TMUX_PROFILE_OPTION}}\t#{session_key_table}\t#{session_panes}\t#{pane_pid}\t#{${GJC_TMUX_BRANCH_OPTION}}\t#{${GJC_TMUX_BRANCH_SLUG_OPTION}}\t#{${GJC_TMUX_PROJECT_OPTION}}\t#{${GJC_TMUX_SESSION_ID_OPTION}}\t#{${GJC_TMUX_SESSION_STATE_FILE_OPTION}}`,
|
|
139
|
+
`#{session_name}\t#{session_windows}\t#{session_attached}\t#{session_created}\t#{${GJC_TMUX_PROFILE_OPTION}}\t#{session_key_table}\t#{session_panes}\t#{pane_pid}\t#{${GJC_TMUX_BRANCH_OPTION}}\t#{${GJC_TMUX_BRANCH_SLUG_OPTION}}\t#{${GJC_TMUX_PROJECT_OPTION}}\t#{${GJC_TMUX_SESSION_ID_OPTION}}\t#{${GJC_TMUX_SESSION_STATE_FILE_OPTION}}\t#{${GJC_TMUX_VERSION_OPTION}}`,
|
|
135
140
|
env,
|
|
136
141
|
);
|
|
137
142
|
}
|
|
@@ -265,6 +270,7 @@ function hydrateSessionFromExactOptions(session: GjcTmuxSessionStatus, env: Node
|
|
|
265
270
|
sessionId: session.sessionId ?? readExactOptionForGc(session.name, GJC_TMUX_SESSION_ID_OPTION, env),
|
|
266
271
|
sessionStateFile:
|
|
267
272
|
session.sessionStateFile ?? readExactOptionForGc(session.name, GJC_TMUX_SESSION_STATE_FILE_OPTION, env),
|
|
273
|
+
version: session.version ?? readExactOptionForGc(session.name, GJC_TMUX_VERSION_OPTION, env),
|
|
268
274
|
};
|
|
269
275
|
}
|
|
270
276
|
|
|
@@ -281,6 +287,7 @@ export function readTmuxSessionTagsForGc(
|
|
|
281
287
|
branchSlug: readExactOptionForGc(sessionName, GJC_TMUX_BRANCH_SLUG_OPTION, env),
|
|
282
288
|
sessionId: readExactOptionForGc(sessionName, GJC_TMUX_SESSION_ID_OPTION, env),
|
|
283
289
|
sessionStateFile: readExactOptionForGc(sessionName, GJC_TMUX_SESSION_STATE_FILE_OPTION, env),
|
|
290
|
+
version: readExactOptionForGc(sessionName, GJC_TMUX_VERSION_OPTION, env),
|
|
284
291
|
createdAt: session?.createdAt,
|
|
285
292
|
attached: session?.attached,
|
|
286
293
|
panePids: session?.panePids,
|
|
@@ -369,6 +369,9 @@
|
|
|
369
369
|
{
|
|
370
370
|
"id": "revision"
|
|
371
371
|
},
|
|
372
|
+
{
|
|
373
|
+
"id": "post-interview"
|
|
374
|
+
},
|
|
372
375
|
{
|
|
373
376
|
"id": "adr"
|
|
374
377
|
},
|
|
@@ -401,6 +404,26 @@
|
|
|
401
404
|
"to": "revision",
|
|
402
405
|
"verb": "write-artifact"
|
|
403
406
|
},
|
|
407
|
+
{
|
|
408
|
+
"from": "revision",
|
|
409
|
+
"to": "post-interview",
|
|
410
|
+
"verb": "write-artifact"
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
"from": "critic",
|
|
414
|
+
"to": "post-interview",
|
|
415
|
+
"verb": "write-artifact"
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
"from": "post-interview",
|
|
419
|
+
"to": "revision",
|
|
420
|
+
"verb": "write-artifact"
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
"from": "post-interview",
|
|
424
|
+
"to": "adr",
|
|
425
|
+
"verb": "write-artifact"
|
|
426
|
+
},
|
|
404
427
|
{
|
|
405
428
|
"from": "revision",
|
|
406
429
|
"to": "adr",
|
|
@@ -435,6 +458,11 @@
|
|
|
435
458
|
"from": "adr",
|
|
436
459
|
"to": "handoff",
|
|
437
460
|
"verb": "handoff"
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
"from": "post-interview",
|
|
464
|
+
"to": "handoff",
|
|
465
|
+
"verb": "handoff"
|
|
438
466
|
}
|
|
439
467
|
],
|
|
440
468
|
"typedArgs": [
|
|
@@ -592,6 +620,7 @@
|
|
|
592
620
|
"architect",
|
|
593
621
|
"critic",
|
|
594
622
|
"revision",
|
|
623
|
+
"post-interview",
|
|
595
624
|
"adr",
|
|
596
625
|
"final"
|
|
597
626
|
],
|