@gajae-code/coding-agent 0.9.6 → 0.10.1
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 +58 -0
- package/dist/types/cli/classify-install-target.d.ts +0 -10
- package/dist/types/cli/daemon-cli.d.ts +2 -0
- package/dist/types/cli/initial-message.d.ts +1 -1
- package/dist/types/cli/malloc-env-guard.d.ts +65 -0
- package/dist/types/cli/malloc-env-guard.test.d.ts +1 -0
- package/dist/types/cli/notify-cli.d.ts +3 -1
- package/dist/types/cli/session-picker.d.ts +4 -3
- package/dist/types/cli/update-cli.d.ts +22 -3
- package/dist/types/cli.d.ts +5 -0
- package/dist/types/commands/daemon.d.ts +6 -2
- package/dist/types/commands/launch.d.ts +1 -0
- package/dist/types/commands/notify.d.ts +6 -0
- package/dist/types/commit/analysis/conventional.d.ts +1 -1
- package/dist/types/commit/analysis/summary.d.ts +1 -1
- package/dist/types/commit/changelog/generate.d.ts +1 -1
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/map-phase.d.ts +1 -1
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +1 -1
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/model-profile-activation.d.ts +1 -0
- package/dist/types/config/settings-schema.d.ts +47 -7
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/coordinator-mcp/model-preset.d.ts +45 -0
- package/dist/types/coordinator-mcp/policy.d.ts +3 -0
- package/dist/types/coordinator-mcp/server.d.ts +79 -6
- package/dist/types/coordinator-mcp/session-reaper.d.ts +53 -0
- package/dist/types/daemon/control-types.d.ts +10 -0
- package/dist/types/daemon/operator-contract.d.ts +47 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.d.ts +4 -0
- package/dist/types/discovery/helpers.d.ts +1 -1
- package/dist/types/discovery/substitute-plugin-root.d.ts +0 -4
- package/dist/types/extensibility/extensions/types.d.ts +3 -1
- package/dist/types/gjc-runtime/launch-tmux.d.ts +26 -7
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +34 -3
- package/dist/types/gjc-runtime/tmux-common.d.ts +6 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation-cli.d.ts +5 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +309 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +49 -17
- package/dist/types/internal-urls/local-protocol.d.ts +13 -9
- package/dist/types/main.d.ts +66 -2
- package/dist/types/modes/acp/acp-agent.d.ts +19 -2
- package/dist/types/modes/acp/acp-mode.d.ts +12 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -1
- package/dist/types/modes/components/custom-editor.d.ts +2 -0
- package/dist/types/modes/components/gajae-pet-widget.d.ts +59 -0
- package/dist/types/modes/components/hook-selector.d.ts +5 -0
- package/dist/types/modes/components/irc-sidebar.d.ts +32 -0
- package/dist/types/modes/components/pet-selector.d.ts +11 -0
- package/dist/types/modes/components/session-selector.d.ts +22 -9
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/status-line/types.d.ts +1 -1
- package/dist/types/modes/components/status-line.d.ts +0 -10
- package/dist/types/modes/components/tool-execution.d.ts +1 -0
- package/dist/types/modes/components/user-message.d.ts +2 -1
- package/dist/types/modes/controllers/event-controller.d.ts +4 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +24 -2
- package/dist/types/modes/irc-observation-ledger.d.ts +22 -0
- package/dist/types/modes/print-mode.d.ts +3 -4
- package/dist/types/modes/rpc/rpc-client.d.ts +7 -1
- package/dist/types/modes/rpc/rpc-mode.d.ts +2 -1
- package/dist/types/modes/rpc/rpc-types.d.ts +17 -1
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +8 -0
- package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +5 -0
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +45 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +42 -2
- package/dist/types/modes/theme/defaults/index.d.ts +568 -568
- package/dist/types/modes/types.d.ts +28 -2
- package/dist/types/modes/utils/abort-message.d.ts +1 -1
- package/dist/types/modes/utils/context-usage.d.ts +4 -19
- package/dist/types/modes/utils/irc-message.d.ts +38 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +13 -4
- package/dist/types/notifications/config.d.ts +34 -0
- package/dist/types/notifications/daemon-paths.d.ts +7 -0
- package/dist/types/notifications/index.d.ts +27 -0
- package/dist/types/notifications/lifecycle-control-runtime.d.ts +12 -4
- package/dist/types/notifications/notification-service.d.ts +144 -0
- package/dist/types/notifications/rate-limit-pool.d.ts +27 -9
- package/dist/types/notifications/telegram-daemon-control.d.ts +6 -0
- package/dist/types/notifications/telegram-daemon.d.ts +130 -3
- package/dist/types/notifications/telegram-reference.d.ts +19 -2
- package/dist/types/notifications/topic-registry.d.ts +24 -4
- package/dist/types/registry/agent-registry.d.ts +2 -0
- package/dist/types/rlm/index.d.ts +1 -1
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +40 -3
- package/dist/types/session/cache-economics.d.ts +79 -0
- package/dist/types/session/context-estimation.d.ts +31 -0
- package/dist/types/session/provider-safety-stop.d.ts +5 -0
- package/dist/types/session/session-manager.d.ts +124 -4
- package/dist/types/session/session-storage.d.ts +155 -13
- package/dist/types/setup/credential-auto-import.d.ts +2 -2
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/task/receipt.d.ts +3 -1
- package/dist/types/task/types.d.ts +8 -0
- package/dist/types/tools/ask-answer-registry.d.ts +5 -0
- package/dist/types/tools/ask.d.ts +25 -1
- package/dist/types/tools/bash.d.ts +1 -1
- package/dist/types/tools/browser/tab-supervisor.d.ts +6 -0
- package/dist/types/tools/index.d.ts +58 -6
- package/dist/types/tools/telegram-send.d.ts +7 -0
- package/dist/types/utils/external-editor.d.ts +2 -0
- package/package.json +9 -9
- package/scripts/g004-tmux-smoke.ts +196 -36
- package/src/cli/daemon-cli.ts +19 -29
- package/src/cli/malloc-env-guard.test.ts +104 -0
- package/src/cli/malloc-env-guard.ts +149 -0
- package/src/cli/notify-cli.ts +78 -14
- package/src/cli/session-picker.ts +23 -40
- package/src/cli/update-cli.ts +56 -37
- package/src/cli.ts +51 -10
- package/src/commands/daemon.ts +19 -5
- package/src/commands/harness.ts +515 -47
- package/src/commands/launch.ts +11 -3
- package/src/commands/notify.ts +5 -1
- package/src/commands/session.ts +6 -1
- package/src/config/keybindings.ts +5 -0
- package/src/config/model-profile-activation.ts +1 -1
- package/src/config/model-profiles.ts +14 -14
- package/src/config/settings-schema.ts +41 -1
- package/src/config/settings.ts +96 -45
- package/src/coordinator/contract.ts +1 -0
- package/src/coordinator-mcp/model-preset.ts +100 -0
- package/src/coordinator-mcp/policy.ts +30 -0
- package/src/coordinator-mcp/server.ts +2881 -425
- package/src/coordinator-mcp/session-reaper.ts +129 -0
- package/src/daemon/control-types.ts +11 -0
- package/src/daemon/operator-contract.ts +121 -0
- package/src/deep-interview/render-middleware.ts +67 -2
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +57 -17
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/payload/sanitize.ts +15 -2
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +5 -1
- package/src/extensibility/extensions/runner.ts +1 -1
- package/src/extensibility/extensions/types.ts +3 -1
- package/src/gjc-runtime/launch-tmux.ts +702 -135
- package/src/gjc-runtime/session-state-sidecar.ts +756 -135
- package/src/gjc-runtime/team-runtime.ts +6 -0
- package/src/gjc-runtime/tmux-common.ts +26 -7
- package/src/gjc-runtime/tmux-gc.ts +52 -9
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +185 -0
- package/src/gjc-runtime/tmux-owner-isolation.ts +2060 -0
- package/src/gjc-runtime/tmux-sessions.ts +871 -68
- package/src/internal-urls/docs-index.generated.ts +12 -11
- package/src/internal-urls/local-protocol.ts +29 -10
- package/src/main.ts +301 -80
- package/src/modes/acp/acp-agent.ts +730 -100
- package/src/modes/acp/acp-mode.ts +29 -2
- package/src/modes/components/assistant-message.ts +32 -3
- package/src/modes/components/bash-execution.ts +43 -13
- package/src/modes/components/custom-editor.ts +5 -0
- package/src/modes/components/footer.ts +3 -3
- package/src/modes/components/gajae-pet-widget.ts +360 -0
- package/src/modes/components/hook-selector.ts +12 -0
- package/src/modes/components/irc-sidebar.ts +206 -0
- package/src/modes/components/pet-selector.ts +55 -0
- package/src/modes/components/session-selector.ts +222 -221
- package/src/modes/components/settings-selector.ts +10 -0
- package/src/modes/components/status-line/segments.ts +13 -6
- package/src/modes/components/status-line/types.ts +1 -1
- package/src/modes/components/status-line.ts +4 -171
- package/src/modes/components/tool-execution.ts +47 -6
- package/src/modes/components/user-message.ts +26 -9
- package/src/modes/controllers/command-controller.ts +28 -2
- package/src/modes/controllers/event-controller.ts +159 -36
- package/src/modes/controllers/extension-ui-controller.ts +37 -18
- package/src/modes/controllers/input-controller.ts +8 -2
- package/src/modes/controllers/selector-controller.ts +62 -15
- package/src/modes/interactive-mode.ts +199 -7
- package/src/modes/irc-observation-ledger.ts +127 -0
- package/src/modes/print-mode.ts +228 -92
- package/src/modes/rpc/rpc-client.ts +58 -1
- package/src/modes/rpc/rpc-mode.ts +286 -85
- package/src/modes/rpc/rpc-types.ts +15 -1
- package/src/modes/shared/agent-wire/command-dispatch.ts +33 -1
- package/src/modes/shared/agent-wire/command-validation.ts +10 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +45 -2
- package/src/modes/shared/agent-wire/scopes.ts +1 -0
- package/src/modes/shared/agent-wire/unattended-session.ts +288 -1
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +130 -20
- package/src/modes/types.ts +34 -2
- package/src/modes/utils/context-usage.ts +56 -92
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/irc-message.ts +138 -0
- package/src/modes/utils/ui-helpers.ts +216 -50
- package/src/notifications/config.ts +44 -0
- package/src/notifications/daemon-paths.ts +8 -0
- package/src/notifications/index.ts +535 -75
- package/src/notifications/lifecycle-control-runtime.ts +930 -77
- package/src/notifications/notification-service.ts +710 -0
- package/src/notifications/rate-limit-pool.ts +46 -12
- package/src/notifications/telegram-daemon-cli.ts +2 -0
- package/src/notifications/telegram-daemon-control.ts +27 -2
- package/src/notifications/telegram-daemon.ts +635 -94
- package/src/notifications/telegram-reference.ts +73 -13
- package/src/notifications/topic-registry.ts +86 -9
- package/src/prompts/system/irc-peer-roster.md +1 -0
- package/src/prompts/system/subagent-system-prompt.md +1 -0
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/prompts/tools/irc.md +1 -1
- package/src/registry/agent-registry.ts +3 -0
- package/src/sdk.ts +52 -1
- package/src/secrets/obfuscator.ts +4 -2
- package/src/session/agent-session.ts +670 -217
- package/src/session/cache-economics.ts +275 -0
- package/src/session/context-estimation.ts +91 -0
- package/src/session/provider-safety-stop.ts +9 -0
- package/src/session/session-dump-format.ts +6 -0
- package/src/session/session-manager.ts +809 -39
- package/src/session/session-storage.ts +582 -38
- package/src/slash-commands/builtin-registry.ts +153 -9
- package/src/slash-commands/helpers/context-report.ts +26 -4
- package/src/task/executor.ts +12 -3
- package/src/task/index.ts +8 -0
- package/src/task/receipt.ts +7 -2
- package/src/task/types.ts +26 -0
- package/src/tools/ask-answer-registry.ts +25 -0
- package/src/tools/ask.ts +355 -33
- package/src/tools/browser/tab-supervisor.ts +63 -6
- package/src/tools/browser.ts +8 -3
- package/src/tools/index.ts +81 -6
- package/src/tools/sqlite-reader.ts +8 -4
- package/src/tui/output-block.ts +18 -6
- package/src/utils/external-editor.ts +6 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentMessage } from "@gajae-code/agent-core";
|
|
2
2
|
import type { AssistantMessage, ImageContent, Message } from "@gajae-code/ai";
|
|
3
|
-
import { type Component, Spacer, Text, TruncatedText } from "@gajae-code/tui";
|
|
3
|
+
import { type Component, Spacer, Text, TruncatedText, type TUI, truncateToWidth } from "@gajae-code/tui";
|
|
4
4
|
import { settings } from "../../config/settings";
|
|
5
5
|
import { resolveSubskillActivationForSkillInvocation } from "../../extensibility/gjc-plugins";
|
|
6
6
|
import { buildSkillPromptMessage, parseSkillInvocations } from "../../extensibility/skills";
|
|
@@ -20,17 +20,69 @@ import { SkillMessageComponent } from "../../modes/components/skill-message";
|
|
|
20
20
|
import { ToolExecutionComponent } from "../../modes/components/tool-execution";
|
|
21
21
|
import { UserMessageComponent } from "../../modes/components/user-message";
|
|
22
22
|
import { theme } from "../../modes/theme/theme";
|
|
23
|
-
import type {
|
|
23
|
+
import type {
|
|
24
|
+
CompactionQueuedMessage,
|
|
25
|
+
InteractiveModeContext,
|
|
26
|
+
IrcArrivalSnapshot,
|
|
27
|
+
TranscriptRebuildPolicy,
|
|
28
|
+
} from "../../modes/types";
|
|
24
29
|
import {
|
|
25
30
|
type CustomMessage,
|
|
26
31
|
isSilentAbort,
|
|
27
32
|
SKILL_PROMPT_MESSAGE_TYPE,
|
|
28
33
|
type SkillPromptDetails,
|
|
29
34
|
} from "../../session/messages";
|
|
30
|
-
import
|
|
35
|
+
import {
|
|
36
|
+
associateSessionMessageViewportAnchorId,
|
|
37
|
+
getSessionMessageEntryId,
|
|
38
|
+
getSessionMessageViewportAnchorId,
|
|
39
|
+
type SessionContext,
|
|
40
|
+
} from "../../session/session-manager";
|
|
31
41
|
import { formatBytes, formatDuration } from "../../tools/render-utils";
|
|
32
42
|
import { buildAbortDisplayMessage } from "./abort-message";
|
|
43
|
+
import {
|
|
44
|
+
formatIrcMessageBlock,
|
|
45
|
+
isIrcCustomType,
|
|
46
|
+
type ParsedIrcMessage,
|
|
47
|
+
parseIrcMessage,
|
|
48
|
+
projectIrcText,
|
|
49
|
+
} from "./irc-message";
|
|
50
|
+
|
|
51
|
+
export type { TranscriptRebuildPolicy } from "../../modes/types";
|
|
52
|
+
|
|
53
|
+
const IRC_INLINE_MAX_RENDER_ROWS = 2_048;
|
|
54
|
+
const IRC_INLINE_MAX_SOURCE_UTF8_BYTES = 64 * 1_024;
|
|
55
|
+
const IRC_INLINE_ELISION = " … message elided …";
|
|
56
|
+
|
|
57
|
+
class BoundedIrcTextComponent implements Component {
|
|
58
|
+
#text: Text;
|
|
59
|
+
#sourceTruncated: boolean;
|
|
60
|
+
|
|
61
|
+
constructor(text: string, sourceTruncated: boolean) {
|
|
62
|
+
this.#text = new Text(text, 0, 0);
|
|
63
|
+
this.#sourceTruncated = sourceTruncated;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
render(width: number): string[] {
|
|
67
|
+
const rendered = this.#text.render(width);
|
|
68
|
+
if (!this.#sourceTruncated && rendered.length <= IRC_INLINE_MAX_RENDER_ROWS) return rendered;
|
|
69
|
+
const lines = rendered.slice(0, IRC_INLINE_MAX_RENDER_ROWS);
|
|
70
|
+
const marker = truncateToWidth(theme.fg("dim", IRC_INLINE_ELISION), width);
|
|
71
|
+
if (lines.length === 0) return [marker];
|
|
72
|
+
if (this.#sourceTruncated && rendered.length < IRC_INLINE_MAX_RENDER_ROWS) lines.push(marker);
|
|
73
|
+
else lines[lines.length - 1] = marker;
|
|
74
|
+
return lines;
|
|
75
|
+
}
|
|
33
76
|
|
|
77
|
+
invalidate(): void {
|
|
78
|
+
this.#text.invalidate();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function prepareTranscriptRebuild(ui: TUI, policy: TranscriptRebuildPolicy): void {
|
|
83
|
+
if (policy === "replace-identity") ui.resetViewportAnchorIntent();
|
|
84
|
+
else ui.prepareViewportAnchorForTranscriptRebuild();
|
|
85
|
+
}
|
|
34
86
|
type TextBlock = { type: "text"; text: string };
|
|
35
87
|
interface RenderInitialMessagesOptions {
|
|
36
88
|
preserveExistingChat?: boolean;
|
|
@@ -118,8 +170,18 @@ function getChatChildTime(component: Component): number {
|
|
|
118
170
|
return chatChildAddedAt.get(component) ?? Date.now();
|
|
119
171
|
}
|
|
120
172
|
|
|
173
|
+
function stableSemanticIdPart(value: string): string {
|
|
174
|
+
let hash = 0xcbf29ce484222325n;
|
|
175
|
+
for (const char of value) {
|
|
176
|
+
hash ^= BigInt(char.codePointAt(0)!);
|
|
177
|
+
hash = BigInt.asUintN(64, hash * 0x100000001b3n);
|
|
178
|
+
}
|
|
179
|
+
return hash.toString(36);
|
|
180
|
+
}
|
|
181
|
+
|
|
121
182
|
export function addChatChild(ctx: InteractiveModeContext, component: Component): void {
|
|
122
183
|
ctx.chatContainer.addChild(component);
|
|
184
|
+
|
|
123
185
|
chatChildAddedAt.set(component, Date.now());
|
|
124
186
|
trimChatChildren(ctx);
|
|
125
187
|
}
|
|
@@ -186,8 +248,114 @@ export function trimChatChildren(ctx: InteractiveModeContext): void {
|
|
|
186
248
|
}
|
|
187
249
|
|
|
188
250
|
export class UiHelpers {
|
|
251
|
+
#renderedIrcInlineComponents = new Map<string, readonly Component[]>();
|
|
252
|
+
#viewportAnchorOccurrences = new WeakMap<object, { base: string; epoch: number; id: string }>();
|
|
253
|
+
#nextViewportAnchorOccurrence = new Map<string, number>();
|
|
254
|
+
#viewportAnchorOccurrenceEpoch = 0;
|
|
255
|
+
#ircSidebarHintShown = false;
|
|
256
|
+
|
|
189
257
|
constructor(private ctx: InteractiveModeContext) {}
|
|
190
258
|
|
|
259
|
+
#resetViewportAnchorOccurrencePass(): void {
|
|
260
|
+
this.#viewportAnchorOccurrenceEpoch += 1;
|
|
261
|
+
this.#nextViewportAnchorOccurrence.clear();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
#viewportAnchorOccurrenceId(message: object, base: string): string {
|
|
265
|
+
const existing = this.#viewportAnchorOccurrences.get(message);
|
|
266
|
+
if (existing?.base === base && existing.epoch === this.#viewportAnchorOccurrenceEpoch) return existing.id;
|
|
267
|
+
const occurrence = this.#nextViewportAnchorOccurrence.get(base) ?? 0;
|
|
268
|
+
this.#nextViewportAnchorOccurrence.set(base, occurrence + 1);
|
|
269
|
+
const id = `${base}:occurrence:${occurrence}`;
|
|
270
|
+
this.#viewportAnchorOccurrences.set(message, {
|
|
271
|
+
base,
|
|
272
|
+
epoch: this.#viewportAnchorOccurrenceEpoch,
|
|
273
|
+
id,
|
|
274
|
+
});
|
|
275
|
+
return id;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
assistantViewportAnchorId(message: AssistantMessage): string {
|
|
279
|
+
const semanticId = getSessionMessageViewportAnchorId(message);
|
|
280
|
+
if (semanticId) return semanticId;
|
|
281
|
+
const entryId = getSessionMessageEntryId(message);
|
|
282
|
+
if (entryId) return `assistant:entry:${entryId}`;
|
|
283
|
+
const base = `assistant:${message.api}:${message.provider}:${message.model}:${message.timestamp}`;
|
|
284
|
+
const id = this.#viewportAnchorOccurrenceId(message, base);
|
|
285
|
+
associateSessionMessageViewportAnchorId(message, id);
|
|
286
|
+
return id;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
#userViewportAnchorId(message: Extract<Message, { role: "user" }>): string {
|
|
290
|
+
const semanticId = getSessionMessageViewportAnchorId(message);
|
|
291
|
+
if (semanticId) return semanticId;
|
|
292
|
+
const entryId = getSessionMessageEntryId(message);
|
|
293
|
+
if (entryId) return `user:entry:${entryId}`;
|
|
294
|
+
const base = `user:local:${message.timestamp}:${stableSemanticIdPart(JSON.stringify(message.content))}`;
|
|
295
|
+
const id = this.#viewportAnchorOccurrenceId(message, base);
|
|
296
|
+
associateSessionMessageViewportAnchorId(message, id);
|
|
297
|
+
return id;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
getRenderedIrcInlineComponents(): Map<string, readonly Component[]> {
|
|
301
|
+
return this.#renderedIrcInlineComponents;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
removeRenderedIrcInlineComponents(observationId: string): readonly Component[] | undefined {
|
|
305
|
+
const components = this.#renderedIrcInlineComponents.get(observationId);
|
|
306
|
+
this.#renderedIrcInlineComponents.delete(observationId);
|
|
307
|
+
return components;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
resetRenderedIrcInlineComponents(): readonly (readonly Component[])[] {
|
|
311
|
+
const components = [...this.#renderedIrcInlineComponents.values()];
|
|
312
|
+
this.#renderedIrcInlineComponents.clear();
|
|
313
|
+
return components;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
#addIrcObservationToChat(message: ParsedIrcMessage, sidebarHint?: string): Component[] {
|
|
317
|
+
const bodyProjection = projectIrcText(message.text, IRC_INLINE_MAX_SOURCE_UTF8_BYTES);
|
|
318
|
+
const block = formatIrcMessageBlock({ ...message, text: bodyProjection.text });
|
|
319
|
+
const components: Component[] = [];
|
|
320
|
+
const header = `${theme.fg("accent", `[IRC] ${block.sender} → ${block.recipient} · ${block.time}`)}${sidebarHint ? theme.fg("dim", sidebarHint) : ""}`;
|
|
321
|
+
const headerComponent = new Text(header, 1, 0);
|
|
322
|
+
addChatChild(this.ctx, headerComponent);
|
|
323
|
+
components.push(headerComponent);
|
|
324
|
+
if (block.bodyLines.length > 0 || bodyProjection.truncated) {
|
|
325
|
+
const bodyComponent = new BoundedIrcTextComponent(
|
|
326
|
+
theme.fg("muted", ` ${block.bodyLines.join("\n ")}`),
|
|
327
|
+
bodyProjection.truncated,
|
|
328
|
+
);
|
|
329
|
+
addChatChild(this.ctx, bodyComponent);
|
|
330
|
+
components.push(bodyComponent);
|
|
331
|
+
}
|
|
332
|
+
return components;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
addLiveIrcObservationToChat(message: ParsedIrcMessage, arrival: IrcArrivalSnapshot): Component[] {
|
|
336
|
+
// Requested-open panels that merely yielded at narrow widths must not
|
|
337
|
+
// advertise the toggle key: pressing it would close the pending request.
|
|
338
|
+
const showSidebarHint =
|
|
339
|
+
!arrival.panelVisible &&
|
|
340
|
+
!arrival.panelRequestedVisible &&
|
|
341
|
+
arrival.sidebarAvailable &&
|
|
342
|
+
Boolean(arrival.resolvedToggleKey) &&
|
|
343
|
+
!this.#ircSidebarHintShown;
|
|
344
|
+
if (showSidebarHint) this.#ircSidebarHintShown = true;
|
|
345
|
+
return this.#addIrcObservationToChat(
|
|
346
|
+
message,
|
|
347
|
+
showSidebarHint ? ` · ${arrival.resolvedToggleKey} opens sidebar` : undefined,
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
addRebuiltIrcObservationToChat(message: ParsedIrcMessage): Component[] {
|
|
352
|
+
return this.#addIrcObservationToChat(message);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
resetIrcSidebarHint(): void {
|
|
356
|
+
this.#ircSidebarHintShown = false;
|
|
357
|
+
}
|
|
358
|
+
|
|
191
359
|
/** Extract text content from a user message */
|
|
192
360
|
getUserMessageText(message: Message): string {
|
|
193
361
|
if (message.role !== "user") return "";
|
|
@@ -304,50 +472,9 @@ export class UiHelpers {
|
|
|
304
472
|
addChatChild(this.ctx, component);
|
|
305
473
|
break;
|
|
306
474
|
}
|
|
307
|
-
if (
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
message.customType === "irc:relay"
|
|
311
|
-
) {
|
|
312
|
-
const details = (
|
|
313
|
-
message as CustomMessage<{
|
|
314
|
-
from?: string;
|
|
315
|
-
to?: string;
|
|
316
|
-
message?: string;
|
|
317
|
-
reply?: string;
|
|
318
|
-
body?: string;
|
|
319
|
-
kind?: "message" | "reply";
|
|
320
|
-
}>
|
|
321
|
-
).details;
|
|
322
|
-
let arrow: string;
|
|
323
|
-
let body: string;
|
|
324
|
-
if (message.customType === "irc:incoming") {
|
|
325
|
-
const peer = details?.from ?? "?";
|
|
326
|
-
body = details?.message ?? "";
|
|
327
|
-
arrow = `⇦ ${peer}`;
|
|
328
|
-
} else if (message.customType === "irc:autoreply") {
|
|
329
|
-
const peer = details?.to ?? "?";
|
|
330
|
-
body = details?.reply ?? "";
|
|
331
|
-
arrow = `⇨ ${peer}`;
|
|
332
|
-
} else {
|
|
333
|
-
const from = details?.from ?? "?";
|
|
334
|
-
const to = details?.to ?? "?";
|
|
335
|
-
body = details?.body ?? "";
|
|
336
|
-
arrow = `${from} ⇨ ${to}`;
|
|
337
|
-
}
|
|
338
|
-
const components: Component[] = [];
|
|
339
|
-
const header = `${theme.fg("accent", `[IRC] ${arrow}`)}`;
|
|
340
|
-
const headerComponent = new Text(header, 1, 0);
|
|
341
|
-
addChatChild(this.ctx, headerComponent);
|
|
342
|
-
components.push(headerComponent);
|
|
343
|
-
if (body) {
|
|
344
|
-
for (const line of body.split("\n")) {
|
|
345
|
-
const lineComponent = new Text(theme.fg("muted", ` ${line}`), 0, 0);
|
|
346
|
-
addChatChild(this.ctx, lineComponent);
|
|
347
|
-
components.push(lineComponent);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
return components;
|
|
475
|
+
if (message.role === "custom" && isIrcCustomType(message.customType)) {
|
|
476
|
+
const parsed = parseIrcMessage(message);
|
|
477
|
+
if (parsed) return this.addRebuiltIrcObservationToChat(parsed);
|
|
351
478
|
}
|
|
352
479
|
if (message.customType === "subagent:steer" || message.customType === "subagent:steer:relay") {
|
|
353
480
|
const details = (
|
|
@@ -421,7 +548,11 @@ export class UiHelpers {
|
|
|
421
548
|
const textContent = this.ctx.getUserMessageText(message);
|
|
422
549
|
if (textContent) {
|
|
423
550
|
const isSynthetic = message.role === "developer" ? true : (message.synthetic ?? false);
|
|
424
|
-
const userComponent = new UserMessageComponent(
|
|
551
|
+
const userComponent = new UserMessageComponent(
|
|
552
|
+
textContent,
|
|
553
|
+
isSynthetic,
|
|
554
|
+
message.role === "user" && !isSynthetic ? this.#userViewportAnchorId(message) : undefined,
|
|
555
|
+
);
|
|
425
556
|
addChatChild(this.ctx, userComponent);
|
|
426
557
|
if (options?.populateHistory && message.role === "user" && !isSynthetic) {
|
|
427
558
|
this.ctx.editor.addToHistory(textContent);
|
|
@@ -430,8 +561,11 @@ export class UiHelpers {
|
|
|
430
561
|
break;
|
|
431
562
|
}
|
|
432
563
|
case "assistant": {
|
|
433
|
-
const assistantComponent = new AssistantMessageComponent(
|
|
434
|
-
|
|
564
|
+
const assistantComponent = new AssistantMessageComponent(
|
|
565
|
+
message,
|
|
566
|
+
this.ctx.hideThinkingBlock,
|
|
567
|
+
() => this.ctx.ui.requestRender(),
|
|
568
|
+
this.assistantViewportAnchorId(message),
|
|
435
569
|
);
|
|
436
570
|
addChatChild(this.ctx, assistantComponent);
|
|
437
571
|
break;
|
|
@@ -469,12 +603,35 @@ export class UiHelpers {
|
|
|
469
603
|
const readToolCallArgs = new Map<string, Record<string, unknown>>();
|
|
470
604
|
const readToolCallAssistantComponents = new Map<string, AssistantMessageComponent>();
|
|
471
605
|
const deferredMessages: AgentMessage[] = [];
|
|
606
|
+
const persistedIrcObservationIds = new Set<string>();
|
|
607
|
+
const now = Date.now();
|
|
608
|
+
this.#resetViewportAnchorOccurrencePass();
|
|
609
|
+
this.#renderedIrcInlineComponents.clear();
|
|
472
610
|
for (const message of sessionContext.messages) {
|
|
473
611
|
// Defer compaction summaries so they render at the bottom (visible after scroll)
|
|
474
612
|
if (message.role === "compactionSummary") {
|
|
475
613
|
deferredMessages.push(message);
|
|
476
614
|
continue;
|
|
477
615
|
}
|
|
616
|
+
if (message.role === "custom" && isIrcCustomType(message.customType)) {
|
|
617
|
+
const parsed = parseIrcMessage(message);
|
|
618
|
+
if (parsed) {
|
|
619
|
+
persistedIrcObservationIds.add(parsed.observationId);
|
|
620
|
+
const record = this.ctx.ircLedger.getRecord(parsed.observationId);
|
|
621
|
+
if (
|
|
622
|
+
record &&
|
|
623
|
+
(record.mode === "persistent" || now < record.expiresAt!) &&
|
|
624
|
+
!this.#renderedIrcInlineComponents.has(record.observationId)
|
|
625
|
+
) {
|
|
626
|
+
this.#renderedIrcInlineComponents.set(
|
|
627
|
+
record.observationId,
|
|
628
|
+
this.addRebuiltIrcObservationToChat(record),
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
634
|
+
|
|
478
635
|
// Assistant messages need special handling for tool calls
|
|
479
636
|
if (message.role === "assistant") {
|
|
480
637
|
this.ctx.addMessageToChat(message);
|
|
@@ -625,6 +782,15 @@ export class UiHelpers {
|
|
|
625
782
|
for (const message of deferredMessages) {
|
|
626
783
|
this.ctx.addMessageToChat(message, options);
|
|
627
784
|
}
|
|
785
|
+
for (const record of this.ctx.ircLedger.getInlineProjection(now)) {
|
|
786
|
+
if (
|
|
787
|
+
persistedIrcObservationIds.has(record.observationId) ||
|
|
788
|
+
this.#renderedIrcInlineComponents.has(record.observationId)
|
|
789
|
+
) {
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
this.#renderedIrcInlineComponents.set(record.observationId, this.addRebuiltIrcObservationToChat(record));
|
|
793
|
+
}
|
|
628
794
|
|
|
629
795
|
this.ctx.pendingTools.clear();
|
|
630
796
|
this.ctx.ui.requestRender();
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import * as crypto from "node:crypto";
|
|
2
2
|
import type { Settings } from "../config/settings";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Env marker set by GJC's own programmatic separate-process child spawn sites
|
|
6
|
+
* (team workers, harness RPC owners) and carrying the spawning session id.
|
|
7
|
+
*
|
|
8
|
+
* Presence — not the value — marks a session as GJC-spawned. It is consumed
|
|
9
|
+
* (read once, then deleted from the child's own env) at startup so it is
|
|
10
|
+
* per-spawn rather than dynastic: a grandchild is marked only if its own spawn
|
|
11
|
+
* site marks it, never by inheriting a marked ancestor's environment.
|
|
12
|
+
*/
|
|
13
|
+
export const SPAWN_PROVENANCE_ENV = "GJC_SPAWNED_BY_SESSION";
|
|
14
|
+
|
|
4
15
|
export interface NotificationConfig {
|
|
5
16
|
enabled: boolean;
|
|
6
17
|
botToken?: string;
|
|
@@ -15,6 +26,12 @@ export interface NotificationConfig {
|
|
|
15
26
|
};
|
|
16
27
|
redact: boolean;
|
|
17
28
|
verbosity: "lean" | "verbose";
|
|
29
|
+
/**
|
|
30
|
+
* Which sessions may register a notification endpoint. `all` (default)
|
|
31
|
+
* preserves historical behavior; `primary` suppresses GJC-spawned children
|
|
32
|
+
* (those carrying {@link SPAWN_PROVENANCE_ENV}) unless they explicitly opt in.
|
|
33
|
+
*/
|
|
34
|
+
sessionScope: "all" | "primary";
|
|
18
35
|
idleTimeoutMs: number;
|
|
19
36
|
rich: {
|
|
20
37
|
enabled: boolean;
|
|
@@ -22,6 +39,15 @@ export interface NotificationConfig {
|
|
|
22
39
|
richDraft: {
|
|
23
40
|
enabled: boolean;
|
|
24
41
|
};
|
|
42
|
+
topics: {
|
|
43
|
+
/**
|
|
44
|
+
* Optional Telegram forum-topic name template with `{repo}`, `{branch}`,
|
|
45
|
+
* and `{title}` placeholders. Unset preserves the built-in
|
|
46
|
+
* `{repo}/{branch} - {title}` composition (with its title/repo/branch
|
|
47
|
+
* fallbacks).
|
|
48
|
+
*/
|
|
49
|
+
nameTemplate?: string;
|
|
50
|
+
};
|
|
25
51
|
}
|
|
26
52
|
|
|
27
53
|
/** Read typed config from Settings. */
|
|
@@ -40,6 +66,7 @@ export function getNotificationConfig(settings: Settings): NotificationConfig {
|
|
|
40
66
|
},
|
|
41
67
|
redact: settings.get("notifications.redact"),
|
|
42
68
|
verbosity: settings.get("notifications.verbosity") === "verbose" ? "verbose" : "lean",
|
|
69
|
+
sessionScope: settings.get("notifications.sessionScope") === "primary" ? "primary" : "all",
|
|
43
70
|
idleTimeoutMs: settings.get("notifications.daemon.idleTimeoutMs"),
|
|
44
71
|
rich: {
|
|
45
72
|
enabled: settings.get("notifications.telegram.rich.enabled"),
|
|
@@ -47,6 +74,9 @@ export function getNotificationConfig(settings: Settings): NotificationConfig {
|
|
|
47
74
|
richDraft: {
|
|
48
75
|
enabled: settings.get("notifications.telegram.richDraft.enabled"),
|
|
49
76
|
},
|
|
77
|
+
topics: {
|
|
78
|
+
nameTemplate: settings.get("notifications.telegram.topics.nameTemplate"),
|
|
79
|
+
},
|
|
50
80
|
};
|
|
51
81
|
}
|
|
52
82
|
|
|
@@ -95,11 +125,25 @@ export function shouldRegisterNotificationsExtension(input: {
|
|
|
95
125
|
parentTaskPrefix?: string;
|
|
96
126
|
/** Role-agent type/name; present for task sessions even if depth metadata is lost. */
|
|
97
127
|
currentAgentType?: string;
|
|
128
|
+
/**
|
|
129
|
+
* True when this session was launched by one of GJC's own programmatic
|
|
130
|
+
* separate-process child spawn sites (marked via {@link SPAWN_PROVENANCE_ENV}).
|
|
131
|
+
* Under `notifications.sessionScope = "primary"` such children are suppressed
|
|
132
|
+
* unless they explicitly opt in, so an interactive parent that fans out work
|
|
133
|
+
* does not flood the paired chat with topics for children the user never
|
|
134
|
+
* asked for. User-opened sessions (CLI/tmux/headless) never carry the marker.
|
|
135
|
+
*/
|
|
136
|
+
spawnedByGjc?: boolean;
|
|
98
137
|
}): boolean {
|
|
99
138
|
if ((input.taskDepth ?? 0) > 0 || input.parentTaskPrefix || input.currentAgentType) return false;
|
|
100
139
|
if (completionNotifyDisabledByEnv(input.env)) return false;
|
|
101
140
|
if (input.env.GJC_NOTIFICATIONS === "0") return false;
|
|
102
141
|
if (input.env.GJC_NOTIFICATIONS === "1" || input.env.GJC_NOTIFICATIONS_TOKEN) return true;
|
|
142
|
+
// Spawned-child suppression sits below explicit opt-in (so Telegram
|
|
143
|
+
// `/session_create` and cold `/session_resume`, which launch with
|
|
144
|
+
// GJC_NOTIFICATIONS=1, keep their fully bidirectional topic) and above global
|
|
145
|
+
// auto-on (so their children stay silent under `primary`).
|
|
146
|
+
if (input.spawnedByGjc && input.cfg?.sessionScope === "primary") return false;
|
|
103
147
|
return input.cfg ? isGloballyConfigured(input.cfg) : false;
|
|
104
148
|
}
|
|
105
149
|
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Owner heartbeat freshness window. A daemon ownership record older than this
|
|
5
|
+
* (without a live pid) is considered stale. Lives here, in the lightweight
|
|
6
|
+
* paths module, so secret-safe consumers (e.g. the notification service) can
|
|
7
|
+
* reuse it without importing the heavy daemon runtime.
|
|
8
|
+
*/
|
|
9
|
+
export const HEARTBEAT_TTL_MS = 20_000;
|
|
10
|
+
|
|
3
11
|
export interface DaemonPaths {
|
|
4
12
|
dir: string;
|
|
5
13
|
lock: string;
|