@gajae-code/coding-agent 0.12.7 → 0.12.10
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 +48 -0
- package/dist/types/cli/notify-cli.d.ts +1 -0
- package/dist/types/commands/notify.d.ts +4 -0
- package/dist/types/commands/sdk.d.ts +23 -0
- package/dist/types/config/file-lock.d.ts +3 -0
- package/dist/types/config/model-discovery-manager.d.ts +6 -1
- package/dist/types/config/model-registry.d.ts +2 -0
- package/dist/types/config/resolve-config-value.d.ts +2 -1
- package/dist/types/config/settings-schema.d.ts +17 -1
- package/dist/types/discovery/agents-md.d.ts +11 -1
- package/dist/types/main.d.ts +1 -0
- package/dist/types/modes/components/hook-selector.d.ts +20 -0
- package/dist/types/modes/components/notifications-settings-editor.d.ts +53 -4
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +4 -2
- package/dist/types/modes/types.d.ts +6 -0
- package/dist/types/sdk/broker/broker.d.ts +5 -1
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +1 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +11 -1
- package/dist/types/sdk/bus/config.d.ts +75 -113
- package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
- package/dist/types/sdk/bus/discord-live-provider.d.ts +8 -2
- package/dist/types/sdk/bus/discord-provider.d.ts +19 -0
- package/dist/types/sdk/bus/index.d.ts +1 -1
- package/dist/types/sdk/bus/notification-orchestration.d.ts +127 -18
- package/dist/types/sdk/bus/notification-service.d.ts +54 -4
- package/dist/types/sdk/bus/session-control.d.ts +8 -3
- package/dist/types/sdk/bus/slack-live-provider.d.ts +9 -2
- package/dist/types/sdk/bus/slack-provider.d.ts +22 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +8 -1
- package/dist/types/sdk/client/discovery.d.ts +2 -1
- package/dist/types/sdk/host/query/handlers.d.ts +2 -0
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/providers.d.ts +22 -0
- package/dist/types/sdk/session-directory.d.ts +1 -1
- package/dist/types/session/internal/managed-session-scope.d.ts +17 -1
- package/dist/types/session/internal/managed-session-storage.d.ts +19 -1
- package/dist/types/session/internal/native-publish-outcome.d.ts +3 -2
- package/dist/types/session/internal/session-open-errors.d.ts +5 -0
- package/dist/types/session/session-manager.d.ts +3 -1
- package/dist/types/session/session-storage.d.ts +2 -0
- package/dist/types/system-prompt.d.ts +12 -1
- package/dist/types/tools/composer-bash-policy.d.ts +9 -1
- package/package.json +10 -8
- package/scripts/build-sdk-package-smoke.ts +51 -0
- package/scripts/capture-notifications-settings-showcase.ts +1 -1
- package/scripts/verify-gjc-sdk-canonicalization.ts +14 -1
- package/src/cli/notify-cli.ts +215 -99
- package/src/commands/notify.ts +19 -8
- package/src/commands/sdk.ts +54 -0
- package/src/config/file-lock.ts +6 -0
- package/src/config/model-discovery-manager.ts +50 -8
- package/src/config/model-profiles.ts +8 -0
- package/src/config/model-registry.ts +866 -76
- package/src/config/resolve-config-value.ts +10 -8
- package/src/config/settings-schema.ts +25 -15
- package/src/config/settings.ts +15 -15
- package/src/discovery/agents-md.ts +76 -19
- package/src/gjc-runtime/psmux-detect.ts +12 -16
- package/src/gjc-runtime/session-state-sidecar.ts +12 -2
- package/src/gjc-runtime/tmux-sessions.ts +8 -1
- package/src/internal-urls/docs-index.generated.ts +10 -9
- package/src/main.ts +43 -7
- package/src/modes/DESIGN.md +11 -11
- package/src/modes/components/extensions/inspector-panel.ts +9 -5
- package/src/modes/components/hook-selector.ts +155 -44
- package/src/modes/components/model-selector.ts +242 -49
- package/src/modes/components/notifications-settings-editor.ts +631 -85
- package/src/modes/controllers/extension-ui-controller.ts +114 -15
- package/src/modes/controllers/selector-controller.ts +525 -93
- package/src/modes/print-mode.ts +3 -0
- package/src/modes/types.ts +6 -0
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/sdk/broker/broker.ts +54 -10
- package/src/sdk/broker/lifecycle-ledger.ts +34 -1
- package/src/sdk/broker/lifecycle.ts +47 -50
- package/src/sdk/bus/chat-daemon-cli.ts +12 -5
- package/src/sdk/bus/chat-daemon-control.ts +35 -6
- package/src/sdk/bus/chat-daemon-runtime.ts +16 -2
- package/src/sdk/bus/config.ts +534 -286
- package/src/sdk/bus/daemon-paths.ts +2 -0
- package/src/sdk/bus/discord-live-provider.ts +57 -2
- package/src/sdk/bus/discord-provider.ts +21 -0
- package/src/sdk/bus/index.ts +97 -35
- package/src/sdk/bus/notification-orchestration.ts +371 -68
- package/src/sdk/bus/notification-service.ts +571 -137
- package/src/sdk/bus/session-control.ts +119 -44
- package/src/sdk/bus/slack-live-provider.ts +78 -2
- package/src/sdk/bus/slack-provider.ts +24 -0
- package/src/sdk/bus/telegram-daemon-cli.ts +9 -2
- package/src/sdk/bus/telegram-daemon-contract.ts +8 -1
- package/src/sdk/bus/telegram-daemon-control.ts +3 -3
- package/src/sdk/bus/telegram-daemon.ts +23 -15
- package/src/sdk/client/discovery.ts +10 -4
- package/src/sdk/host/query/handlers.ts +20 -1
- package/src/sdk/index.ts +1 -0
- package/src/sdk/protocol/operation-inventory.generated.json +18 -0
- package/src/sdk/protocol/operation-registry.ts +5 -2
- package/src/sdk/providers.ts +51 -0
- package/src/sdk/session-directory.ts +1 -0
- package/src/sdk/session.ts +34 -14
- package/src/session/agent-session.ts +109 -37
- package/src/session/internal/managed-session-scope.ts +981 -450
- package/src/session/internal/managed-session-storage.ts +418 -47
- package/src/session/internal/native-publish-outcome.ts +15 -3
- package/src/session/internal/session-open-errors.ts +11 -0
- package/src/session/session-manager.ts +27 -3
- package/src/session/session-storage.ts +72 -5
- package/src/slash-commands/builtin-registry.ts +117 -15
- package/src/system-prompt.ts +28 -15
- package/src/task/index.ts +9 -3
- package/src/tools/ask.ts +1 -1
- package/src/tools/bash.ts +4 -2
- package/src/tools/composer-bash-policy.ts +21 -5
- package/src/tools/telegram-send.ts +4 -2
- package/src/utils/command-args.ts +19 -30
- package/src/utils/lang-from-path.ts +2 -4
|
@@ -16,6 +16,7 @@ export interface DaemonPaths {
|
|
|
16
16
|
ownership: string;
|
|
17
17
|
roots: string;
|
|
18
18
|
steal: string;
|
|
19
|
+
diagnostic: string;
|
|
19
20
|
aliases: string;
|
|
20
21
|
seenUpdates: string;
|
|
21
22
|
}
|
|
@@ -31,6 +32,7 @@ export function daemonPaths(agentDir: string): DaemonPaths {
|
|
|
31
32
|
roots: path.join(dir, "telegram-daemon.roots.json"),
|
|
32
33
|
steal: path.join(dir, "telegram-daemon.steal"),
|
|
33
34
|
aliases: path.join(dir, "telegram-callback-aliases.json"),
|
|
35
|
+
diagnostic: path.join(dir, "telegram-daemon.diagnostics.json"),
|
|
34
36
|
seenUpdates: path.join(dir, "telegram-seen-updates.json"),
|
|
35
37
|
};
|
|
36
38
|
}
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
DiscordConfigurationProbeResult,
|
|
3
|
+
DiscordDiagnosticProvider,
|
|
4
|
+
DiscordInboundEvent,
|
|
5
|
+
DiscordMessageComponent,
|
|
6
|
+
DiscordOneShotTestResult,
|
|
7
|
+
DiscordProvider,
|
|
8
|
+
DiscordThread,
|
|
9
|
+
} from "./discord-provider";
|
|
2
10
|
|
|
3
11
|
const API_BASE = "https://discord.com/api/v10";
|
|
4
12
|
const GATEWAY_INTENTS = 1 + 512 + 32_768;
|
|
@@ -35,7 +43,7 @@ export interface DiscordLiveProviderOptions {
|
|
|
35
43
|
type JsonRecord = Record<string, unknown>;
|
|
36
44
|
|
|
37
45
|
/** Discord REST/Gateway implementation. The only credential is held privately and is never emitted. */
|
|
38
|
-
export class DiscordLiveProvider implements DiscordProvider {
|
|
46
|
+
export class DiscordLiveProvider implements DiscordProvider, DiscordDiagnosticProvider {
|
|
39
47
|
readonly applicationId: string;
|
|
40
48
|
readonly #token: string;
|
|
41
49
|
readonly #fetch: DiscordFetch;
|
|
@@ -230,6 +238,53 @@ export class DiscordLiveProvider implements DiscordProvider {
|
|
|
230
238
|
return null;
|
|
231
239
|
}
|
|
232
240
|
|
|
241
|
+
async probeConfiguration(signal?: AbortSignal): Promise<DiscordConfigurationProbeResult> {
|
|
242
|
+
if (signal?.aborted) return { ok: false, detail: "Discord configuration probe cancelled." };
|
|
243
|
+
try {
|
|
244
|
+
const currentUser = await this.#request("/users/@me", { signal });
|
|
245
|
+
const botUserId = this.#string(currentUser, "id");
|
|
246
|
+
if (!botUserId) return { ok: false, detail: "Discord returned an invalid bot identity." };
|
|
247
|
+
const application = await this.#request("/applications/@me", { signal });
|
|
248
|
+
const applicationId = this.#string(application, "id");
|
|
249
|
+
if (!applicationId || applicationId !== this.applicationId) {
|
|
250
|
+
return { ok: false, detail: "Discord application identity does not match the configured application ID." };
|
|
251
|
+
}
|
|
252
|
+
return { ok: true, detail: "Discord bot and application credentials are valid.", botUserId };
|
|
253
|
+
} catch (error) {
|
|
254
|
+
if (signal?.aborted) return { ok: false, detail: "Discord configuration probe cancelled." };
|
|
255
|
+
const detail = error instanceof Error ? error.message : "Discord configuration probe failed.";
|
|
256
|
+
return { ok: false, detail };
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async sendOneShotTest(input: {
|
|
261
|
+
channelId: string;
|
|
262
|
+
message: string;
|
|
263
|
+
signal?: AbortSignal;
|
|
264
|
+
}): Promise<DiscordOneShotTestResult> {
|
|
265
|
+
if (input.signal?.aborted) return { ok: false, detail: "Discord notification test cancelled." };
|
|
266
|
+
try {
|
|
267
|
+
const response = await this.#request(`/channels/${input.channelId}/messages`, {
|
|
268
|
+
method: "POST",
|
|
269
|
+
body: JSON.stringify({ content: input.message }),
|
|
270
|
+
signal: input.signal,
|
|
271
|
+
});
|
|
272
|
+
const messageId = this.#string(response, "id");
|
|
273
|
+
if (!messageId) {
|
|
274
|
+
return {
|
|
275
|
+
ok: false,
|
|
276
|
+
detail: "Discord may have accepted the message but returned no message receipt.",
|
|
277
|
+
uncertain: true,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
return { ok: true, detail: "Discord notification test delivered.", messageId };
|
|
281
|
+
} catch (error) {
|
|
282
|
+
if (input.signal?.aborted) return { ok: false, detail: "Discord notification test cancelled." };
|
|
283
|
+
const detail = error instanceof Error ? error.message : "Discord notification test failed.";
|
|
284
|
+
return { ok: false, detail, uncertain: !detail.startsWith("Discord API request failed") };
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
233
288
|
async start(onEvent: (event: DiscordInboundEvent) => Promise<void>): Promise<void> {
|
|
234
289
|
if (!this.#stopped) return;
|
|
235
290
|
this.#onEvent = onEvent;
|
|
@@ -31,6 +31,27 @@ export interface DiscordMessageComponent {
|
|
|
31
31
|
}>;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
export interface DiscordConfigurationProbeResult {
|
|
35
|
+
ok: boolean;
|
|
36
|
+
detail: string;
|
|
37
|
+
botUserId?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface DiscordOneShotTestResult {
|
|
41
|
+
ok: boolean;
|
|
42
|
+
detail: string;
|
|
43
|
+
messageId?: string;
|
|
44
|
+
uncertain?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface DiscordDiagnosticProvider {
|
|
48
|
+
probeConfiguration(signal?: AbortSignal): Promise<DiscordConfigurationProbeResult>;
|
|
49
|
+
sendOneShotTest(input: {
|
|
50
|
+
channelId: string;
|
|
51
|
+
message: string;
|
|
52
|
+
signal?: AbortSignal;
|
|
53
|
+
}): Promise<DiscordOneShotTestResult>;
|
|
54
|
+
}
|
|
34
55
|
export interface DiscordProvider {
|
|
35
56
|
readonly applicationId: string;
|
|
36
57
|
readonly botUserId: string;
|
package/src/sdk/bus/index.ts
CHANGED
|
@@ -67,6 +67,7 @@ import { CursorRegistry, QueryHandlers, RevisionStore, type SessionSurface } fro
|
|
|
67
67
|
import { projectQ10Models } from "../models.js";
|
|
68
68
|
import { PROMPT_CLIENT_REF_MAX_LENGTH, type SdkPromptTerminalOutcome } from "../prompt-status";
|
|
69
69
|
import { OPERATIONS } from "../protocol/operation-registry";
|
|
70
|
+
import { ActiveProviderResolutionError } from "../providers.js";
|
|
70
71
|
import {
|
|
71
72
|
lifecycleStartupCapabilityForApi,
|
|
72
73
|
normalizeSdkStartupFailure,
|
|
@@ -75,13 +76,12 @@ import {
|
|
|
75
76
|
import { registerTelegramFileSink } from "./attachment-registry";
|
|
76
77
|
import { ensureDiscordDaemon, ensureSlackDaemon } from "./chat-daemon-control";
|
|
77
78
|
import {
|
|
79
|
+
getCurrentTelegramActivationMarker,
|
|
78
80
|
getNotificationConfig,
|
|
79
|
-
|
|
80
|
-
isSessionNotificationsEnabled,
|
|
81
|
-
isSlackConfigured,
|
|
82
|
-
isTelegramConfigured,
|
|
81
|
+
isProviderEffectivelyEnabled,
|
|
83
82
|
type NotificationConfig,
|
|
84
83
|
type NotificationSettingsReader,
|
|
84
|
+
resolveGenericNotificationSessionEligibility,
|
|
85
85
|
sessionTag,
|
|
86
86
|
} from "./config";
|
|
87
87
|
import { telegramControlCommandUsage } from "./config-commands";
|
|
@@ -93,6 +93,7 @@ import {
|
|
|
93
93
|
import { imageAttachmentsFromMessage, notificationActionPayload, summaryFromMessage, truncate } from "./helpers";
|
|
94
94
|
import { createKindAwareReconciliation } from "./kind-aware-reconciliation";
|
|
95
95
|
import { assertNativeRuntimeCompatibility } from "./native-runtime-compatibility";
|
|
96
|
+
import { proposedTelegramIdentity } from "./notification-orchestration";
|
|
96
97
|
import { createPromptReconciliation, sanitizePromptFailure } from "./prompt-reconciliation";
|
|
97
98
|
import { createReconciliationStore } from "./reconciliation-store";
|
|
98
99
|
import { NotificationSessionController, type NotificationSessionRuntime } from "./session-control";
|
|
@@ -987,7 +988,7 @@ interface SessionRuntime {
|
|
|
987
988
|
serverStopped: boolean;
|
|
988
989
|
brokerRegistrationReleased: boolean;
|
|
989
990
|
/** Managed Telegram root registration released during terminal teardown. */
|
|
990
|
-
notificationRootRegistration?: { settings: Settings; cwd: string; registrationToken
|
|
991
|
+
notificationRootRegistration?: { settings: Settings; cwd: string; registrationToken: string };
|
|
991
992
|
verbosity: "lean" | "verbose";
|
|
992
993
|
sessionTag: string;
|
|
993
994
|
/** Whether the agent loop is currently running (drives the typing indicator). */
|
|
@@ -2101,6 +2102,13 @@ function sdkQuerySurface(
|
|
|
2101
2102
|
promptTerminalOutcomeVersion: 1,
|
|
2102
2103
|
}),
|
|
2103
2104
|
getAuthProviders: () => [...new Set(ctx.modelRegistry.getAll().map(model => model.provider))],
|
|
2105
|
+
getActiveProviders: () => {
|
|
2106
|
+
try {
|
|
2107
|
+
return ctx.modelRegistry.getActiveProviders();
|
|
2108
|
+
} catch {
|
|
2109
|
+
throw new ActiveProviderResolutionError();
|
|
2110
|
+
}
|
|
2111
|
+
},
|
|
2104
2112
|
getTools: () => {
|
|
2105
2113
|
const tools = typeof (ctx as Partial<ExtensionContext>).getAllTools === "function" ? ctx.getAllTools() : [];
|
|
2106
2114
|
return tools.length > 0 ? tools : (getInstalledDefinitions("host_tools") ?? []);
|
|
@@ -3193,8 +3201,8 @@ export async function ensureConfiguredProviderDaemons(
|
|
|
3193
3201
|
configuredSettings,
|
|
3194
3202
|
) => (provider === "discord" ? ensureDiscordDaemon(configuredSettings) : ensureSlackDaemon(configuredSettings)),
|
|
3195
3203
|
): Promise<void> {
|
|
3196
|
-
if (
|
|
3197
|
-
if (
|
|
3204
|
+
if (isProviderEffectivelyEnabled(cfg, "discord")) await ensureProviderDaemon("discord", settings);
|
|
3205
|
+
if (isProviderEffectivelyEnabled(cfg, "slack")) await ensureProviderDaemon("slack", settings);
|
|
3198
3206
|
}
|
|
3199
3207
|
|
|
3200
3208
|
/**
|
|
@@ -3241,12 +3249,14 @@ export function createNotificationsExtension(
|
|
|
3241
3249
|
new NotificationSessionController({
|
|
3242
3250
|
eligible: true,
|
|
3243
3251
|
getConfig: () => resolveSettings(options.settings).cfg,
|
|
3252
|
+
spawnedByGjc: options.spawnedByGjc,
|
|
3244
3253
|
});
|
|
3245
3254
|
|
|
3246
3255
|
// Failed terminal teardown remains fenced from normal runtime lookup while the
|
|
3247
3256
|
// exact runtime object retains authority for an explicit idempotent retry.
|
|
3248
3257
|
const cleanupRetries = new Map<string, SessionRuntime>();
|
|
3249
3258
|
const sessionStartPromises = new Map<string, Promise<SessionStartResult>>();
|
|
3259
|
+
const forceIsolatedChatSessions = new Set<string>();
|
|
3250
3260
|
const branchStartupTasks = new Set<Promise<void>>();
|
|
3251
3261
|
const sessionLifecycleTasks = new Set<Promise<void>>();
|
|
3252
3262
|
let activeRuntimeId: string | undefined;
|
|
@@ -3285,18 +3295,34 @@ export function createNotificationsExtension(
|
|
|
3285
3295
|
? "blocked_identity"
|
|
3286
3296
|
: "ready";
|
|
3287
3297
|
}
|
|
3298
|
+
type ConfiguredDaemonOwnerResult = "ready" | "blocked_identity" | "blocked_identity_with_sibling";
|
|
3288
3299
|
async function ensureConfiguredDaemonOwners(
|
|
3289
3300
|
settings: Settings,
|
|
3290
3301
|
cfg: NotificationConfig,
|
|
3291
3302
|
cwd: string,
|
|
3292
3303
|
id: string,
|
|
3293
3304
|
onRegistered?: (registrationToken: string) => void,
|
|
3294
|
-
): Promise<
|
|
3295
|
-
if (
|
|
3296
|
-
|
|
3305
|
+
): Promise<ConfiguredDaemonOwnerResult> {
|
|
3306
|
+
if (isProviderEffectivelyEnabled(cfg, "telegram")) {
|
|
3307
|
+
const telegramMarker = getCurrentTelegramActivationMarker(cfg);
|
|
3308
|
+
if (telegramMarker) {
|
|
3309
|
+
if (!isProviderEffectivelyEnabled(cfg, "discord") && !isProviderEffectivelyEnabled(cfg, "slack")) {
|
|
3310
|
+
return "blocked_identity";
|
|
3311
|
+
}
|
|
3312
|
+
await ensureConfiguredProviderDaemons(settings, cfg, options.ensureProviderDaemon);
|
|
3313
|
+
return "blocked_identity_with_sibling";
|
|
3314
|
+
}
|
|
3315
|
+
const telegram = await ensureTelegramOwner(settings, cwd, id, onRegistered);
|
|
3316
|
+
if (telegram === "blocked_identity") {
|
|
3317
|
+
if (!isProviderEffectivelyEnabled(cfg, "discord") && !isProviderEffectivelyEnabled(cfg, "slack")) {
|
|
3318
|
+
return "blocked_identity";
|
|
3319
|
+
}
|
|
3320
|
+
await ensureConfiguredProviderDaemons(settings, cfg, options.ensureProviderDaemon);
|
|
3321
|
+
return "blocked_identity_with_sibling";
|
|
3322
|
+
}
|
|
3297
3323
|
}
|
|
3298
3324
|
await ensureConfiguredProviderDaemons(settings, cfg, options.ensureProviderDaemon);
|
|
3299
|
-
return
|
|
3325
|
+
return "ready";
|
|
3300
3326
|
}
|
|
3301
3327
|
const identityControlOperations = new Set([
|
|
3302
3328
|
"session.new",
|
|
@@ -3484,7 +3510,7 @@ export function createNotificationsExtension(
|
|
|
3484
3510
|
const id = sessionId(ctx);
|
|
3485
3511
|
const lifecycleRequestId = safeLifecycleRequestId(process.env.GJC_LIFECYCLE_REQUEST_ID);
|
|
3486
3512
|
const { settings, cfg, settingsAvailable } = resolveSettings(options.settings);
|
|
3487
|
-
const notificationsEnabledForSession = controller.query(ctx).
|
|
3513
|
+
const notificationsEnabledForSession = controller.query(ctx).genericSessionEnabled;
|
|
3488
3514
|
const sdkEnabledForSession =
|
|
3489
3515
|
(options.sdkHostModeSupported ?? true) && shouldHostSdk(settings, isNotificationEligibleContext(ctx));
|
|
3490
3516
|
const lifecycleRequired = lifecycleStartupCapability !== undefined;
|
|
@@ -3525,6 +3551,25 @@ export function createNotificationsExtension(
|
|
|
3525
3551
|
}
|
|
3526
3552
|
|
|
3527
3553
|
const stateRoot = path.join(ctx.cwd, ".gjc", "state");
|
|
3554
|
+
let isolateChatEndpoint = forceIsolatedChatSessions.delete(id);
|
|
3555
|
+
if (
|
|
3556
|
+
!isolateChatEndpoint &&
|
|
3557
|
+
notificationsEnabledForSession &&
|
|
3558
|
+
settingsAvailable &&
|
|
3559
|
+
settings &&
|
|
3560
|
+
isProviderEffectivelyEnabled(cfg, "telegram") &&
|
|
3561
|
+
(isProviderEffectivelyEnabled(cfg, "discord") || isProviderEffectivelyEnabled(cfg, "slack")) &&
|
|
3562
|
+
typeof cfg.botToken === "string" &&
|
|
3563
|
+
typeof cfg.chatId === "string"
|
|
3564
|
+
) {
|
|
3565
|
+
const marker = getCurrentTelegramActivationMarker(cfg);
|
|
3566
|
+
if (marker) isolateChatEndpoint = true;
|
|
3567
|
+
else {
|
|
3568
|
+
const identity = await proposedTelegramIdentity({ settings, botToken: cfg.botToken, chatId: cfg.chatId });
|
|
3569
|
+
isolateChatEndpoint = identity.status === "foreign" || identity.status === "unknown";
|
|
3570
|
+
}
|
|
3571
|
+
}
|
|
3572
|
+
const endpointStateRoot = isolateChatEndpoint ? path.join(stateRoot, "chat") : stateRoot;
|
|
3528
3573
|
const lifecycleAgentDir = lifecycleRequired ? settings?.getAgentDir?.() : undefined;
|
|
3529
3574
|
if (lifecycleRequired && !lifecycleAgentDir)
|
|
3530
3575
|
return failLifecycleStartup("failed", "Lifecycle SDK startup requires an agent directory.");
|
|
@@ -3546,7 +3591,7 @@ export function createNotificationsExtension(
|
|
|
3546
3591
|
nativeVersion: nativeBuildInfo().version,
|
|
3547
3592
|
notificationServer: NotificationServer.prototype,
|
|
3548
3593
|
});
|
|
3549
|
-
server = new NotificationServer(id, token,
|
|
3594
|
+
server = new NotificationServer(id, token, endpointStateRoot, true);
|
|
3550
3595
|
} catch (error) {
|
|
3551
3596
|
if (lifecycleRequired) return failLifecycleStartup("failed", error);
|
|
3552
3597
|
throw error;
|
|
@@ -5006,18 +5051,26 @@ export function createNotificationsExtension(
|
|
|
5006
5051
|
if (notificationsEnabledForSession && settingsAvailable && settings) {
|
|
5007
5052
|
try {
|
|
5008
5053
|
let registrationToken: string | undefined;
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
) {
|
|
5054
|
+
const ownership = await ensureConfiguredDaemonOwners(settings, cfg, ctx.cwd, id, token => {
|
|
5055
|
+
registrationToken = token;
|
|
5056
|
+
});
|
|
5057
|
+
if (ownership === "blocked_identity") {
|
|
5014
5058
|
const result = failLifecycleStartup("failed", "Telegram daemon ownership is blocked.");
|
|
5015
5059
|
finishStartup(result);
|
|
5016
5060
|
await cleanupAbandonedStartup();
|
|
5017
5061
|
return result;
|
|
5018
5062
|
}
|
|
5019
|
-
if (
|
|
5063
|
+
if (ownership === "blocked_identity_with_sibling" && !isolateChatEndpoint) {
|
|
5064
|
+
await cleanupAbandonedStartup();
|
|
5065
|
+
if (sessionStartPromises.get(id) === startSettled.promise) sessionStartPromises.delete(id);
|
|
5066
|
+
forceIsolatedChatSessions.add(id);
|
|
5067
|
+
const result = await startSession(ctx);
|
|
5068
|
+
finishStartup(result);
|
|
5069
|
+
return result;
|
|
5070
|
+
}
|
|
5071
|
+
if (registrationToken !== undefined) {
|
|
5020
5072
|
runtime.notificationRootRegistration = { settings, cwd: ctx.cwd, registrationToken };
|
|
5073
|
+
}
|
|
5021
5074
|
} catch (error) {
|
|
5022
5075
|
const result = failLifecycleStartup("failed", error);
|
|
5023
5076
|
finishStartup(result);
|
|
@@ -5059,8 +5112,8 @@ export function createNotificationsExtension(
|
|
|
5059
5112
|
throwIfLifecycleStopped();
|
|
5060
5113
|
const index = await new SessionIndex(agentDir).open();
|
|
5061
5114
|
throwIfLifecycleStopped();
|
|
5062
|
-
const locator = { repo: path.resolve(ctx.cwd), stateRoot };
|
|
5063
|
-
const endpointMtimeMs = fs.statSync(path.join(
|
|
5115
|
+
const locator = { repo: path.resolve(ctx.cwd), stateRoot: endpointStateRoot };
|
|
5116
|
+
const endpointMtimeMs = fs.statSync(path.join(endpointStateRoot, "sdk", `${id}.json`)).mtimeMs;
|
|
5064
5117
|
await host.registerWithBroker({
|
|
5065
5118
|
// The endpoint is written before registration. Its exact mtime
|
|
5066
5119
|
// binds this index generation to that discovery record.
|
|
@@ -5294,6 +5347,19 @@ export function createNotificationsExtension(
|
|
|
5294
5347
|
return result.status === "started" || result.status === "already" ? "started" : result.status;
|
|
5295
5348
|
},
|
|
5296
5349
|
stop: async binding => await stopSession(binding.sessionId, "notifications"),
|
|
5350
|
+
isolateTelegram: async binding => {
|
|
5351
|
+
const runtime = runtimes.get(binding.sessionId);
|
|
5352
|
+
if (runtime) {
|
|
5353
|
+
const stopped = await stopSession(binding.sessionId, "session", runtime);
|
|
5354
|
+
if (!stopped || runtimes.has(binding.sessionId) || cleanupRetries.has(binding.sessionId)) return "failed";
|
|
5355
|
+
}
|
|
5356
|
+
forceIsolatedChatSessions.add(binding.sessionId);
|
|
5357
|
+
const result = await startSession(binding.context);
|
|
5358
|
+
if (result.status !== "started" && result.status !== "already") {
|
|
5359
|
+
forceIsolatedChatSessions.delete(binding.sessionId);
|
|
5360
|
+
}
|
|
5361
|
+
return result.status === "started" || result.status === "already" ? "started" : result.status;
|
|
5362
|
+
},
|
|
5297
5363
|
refreshPolicy: (binding, policy) => {
|
|
5298
5364
|
const runtime = runtimes.get(binding.sessionId);
|
|
5299
5365
|
if (!runtime) return;
|
|
@@ -5339,8 +5405,8 @@ export function createNotificationsExtension(
|
|
|
5339
5405
|
registrationToken = token;
|
|
5340
5406
|
});
|
|
5341
5407
|
const runtime = runtimes.get(binding.sessionId);
|
|
5342
|
-
const configured =
|
|
5343
|
-
if (result === "ready" && runtime && !runtime.stopping && configured) {
|
|
5408
|
+
const configured = isProviderEffectivelyEnabled(resolveSettings(options.settings).cfg, "telegram");
|
|
5409
|
+
if (result === "ready" && runtime && !runtime.stopping && configured && registrationToken !== undefined) {
|
|
5344
5410
|
runtime.notificationRootRegistration = { settings, cwd: binding.cwd, registrationToken };
|
|
5345
5411
|
} else if (registrationToken !== undefined) {
|
|
5346
5412
|
await unregisterNotificationRoot({
|
|
@@ -5364,11 +5430,14 @@ export function createNotificationsExtension(
|
|
|
5364
5430
|
const id = sessionId(ctx);
|
|
5365
5431
|
const command = args.trim().split(/\s+/, 1)[0]?.toLowerCase() || "status";
|
|
5366
5432
|
const resolved = resolveSettings(options.settings);
|
|
5367
|
-
const
|
|
5433
|
+
const manualEligibilityEnv =
|
|
5434
|
+
process.env.GJC_NOTIFICATIONS === "0" ? { ...process.env, GJC_NOTIFICATIONS: undefined } : process.env;
|
|
5435
|
+
const enabledWithoutLocalOff = resolveGenericNotificationSessionEligibility({
|
|
5368
5436
|
cfg: resolved.cfg,
|
|
5369
|
-
env:
|
|
5437
|
+
env: manualEligibilityEnv,
|
|
5370
5438
|
sessionDisabled: false,
|
|
5371
|
-
|
|
5439
|
+
spawnedByGjc: options.spawnedByGjc,
|
|
5440
|
+
}).enabled;
|
|
5372
5441
|
|
|
5373
5442
|
if (command === "off") {
|
|
5374
5443
|
const result = await controller.setLocalEnabled(ctx, false);
|
|
@@ -5386,13 +5455,6 @@ export function createNotificationsExtension(
|
|
|
5386
5455
|
ctx.ui.notify("Notifications are disabled for subagent sessions.", "warning");
|
|
5387
5456
|
return;
|
|
5388
5457
|
}
|
|
5389
|
-
if (process.env.GJC_NOTIFICATIONS === "0") {
|
|
5390
|
-
ctx.ui.notify(
|
|
5391
|
-
"Notifications remain disabled: GJC_NOTIFICATIONS=0 is an authoritative opt-out.",
|
|
5392
|
-
"warning",
|
|
5393
|
-
);
|
|
5394
|
-
return;
|
|
5395
|
-
}
|
|
5396
5458
|
if (!enabledWithoutLocalOff) {
|
|
5397
5459
|
ctx.ui.notify(
|
|
5398
5460
|
"Notifications are not configured. Run `gjc notify setup` or set GJC_NOTIFICATIONS=1.",
|
|
@@ -5401,7 +5463,7 @@ export function createNotificationsExtension(
|
|
|
5401
5463
|
return;
|
|
5402
5464
|
}
|
|
5403
5465
|
const result = await controller.setLocalEnabled(ctx, true);
|
|
5404
|
-
const enabled = result.status.running && result.status.
|
|
5466
|
+
const enabled = result.status.running && result.status.genericSessionEnabled;
|
|
5405
5467
|
const rotated = sessionId(ctx) !== id;
|
|
5406
5468
|
if (rotated) await stopSession(id);
|
|
5407
5469
|
const failed = result.outcome === "failed" || (!enabled && !rotated && activeRuntimeId !== id);
|
|
@@ -5426,7 +5488,7 @@ export function createNotificationsExtension(
|
|
|
5426
5488
|
const status = controller.query(ctx);
|
|
5427
5489
|
const runtime = runtimes.get(id);
|
|
5428
5490
|
ctx.ui.notify(
|
|
5429
|
-
`Notifications ${status.running ? "running" : status.
|
|
5491
|
+
`Notifications ${status.running ? "running" : status.genericSessionEnabled ? "enabled" : "disabled"} for this session; admission ${status.genericEligibilitySource}; redaction ${(runtime?.redact ?? resolved.cfg.redact) ? "on" : "off"}; verbosity ${runtime?.verbosity ?? resolved.cfg.verbosity}${status.locallyEnabled ? "" : "; locally off"}.`,
|
|
5430
5492
|
"info",
|
|
5431
5493
|
);
|
|
5432
5494
|
},
|