@sayknow-cli/coding-agent 0.2.7 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/types/async/job-manager.d.ts +3 -1
  3. package/dist/types/cli/daemon-cli.d.ts +25 -0
  4. package/dist/types/cli/notify-cli.d.ts +23 -0
  5. package/dist/types/cli/setup-cli.d.ts +20 -1
  6. package/dist/types/commands/daemon.d.ts +41 -0
  7. package/dist/types/commands/notify.d.ts +41 -0
  8. package/dist/types/config/model-profile-activation.d.ts +12 -0
  9. package/dist/types/config/model-profiles.d.ts +2 -1
  10. package/dist/types/config/model-registry.d.ts +3 -3
  11. package/dist/types/config/models-config-schema.d.ts +5 -0
  12. package/dist/types/config/settings-schema.d.ts +38 -0
  13. package/dist/types/coordinator/contract.d.ts +1 -1
  14. package/dist/types/daemon/builtin.d.ts +20 -0
  15. package/dist/types/daemon/control-types.d.ts +57 -0
  16. package/dist/types/daemon/runtime.d.ts +25 -0
  17. package/dist/types/defaults/skc-defaults.d.ts +13 -2
  18. package/dist/types/extensibility/extensions/types.d.ts +8 -0
  19. package/dist/types/modes/components/oauth-selector.d.ts +2 -0
  20. package/dist/types/modes/controllers/selector-controller.d.ts +2 -2
  21. package/dist/types/modes/interactive-mode.d.ts +1 -1
  22. package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +10 -0
  23. package/dist/types/modes/types.d.ts +7 -1
  24. package/dist/types/notifications/config-commands.d.ts +26 -0
  25. package/dist/types/notifications/config.d.ts +61 -0
  26. package/dist/types/notifications/helpers.d.ts +55 -0
  27. package/dist/types/notifications/html-format.d.ts +62 -0
  28. package/dist/types/notifications/index.d.ts +28 -0
  29. package/dist/types/notifications/rate-limit-pool.d.ts +93 -0
  30. package/dist/types/notifications/telegram-cli.d.ts +19 -0
  31. package/dist/types/notifications/telegram-daemon-cli.d.ts +11 -0
  32. package/dist/types/notifications/telegram-daemon-control.d.ts +56 -0
  33. package/dist/types/notifications/telegram-daemon.d.ts +276 -0
  34. package/dist/types/notifications/telegram-reference.d.ts +111 -0
  35. package/dist/types/notifications/threaded-inbound.d.ts +58 -0
  36. package/dist/types/notifications/threaded-render.d.ts +66 -0
  37. package/dist/types/notifications/topic-registry.d.ts +67 -0
  38. package/dist/types/rlm/index.d.ts +12 -0
  39. package/dist/types/session/agent-session.d.ts +39 -2
  40. package/dist/types/session/auth-storage.d.ts +1 -1
  41. package/dist/types/setup/credential-auto-import.d.ts +63 -0
  42. package/dist/types/setup/credential-import.d.ts +3 -0
  43. package/dist/types/setup/host-plugin-setup.d.ts +39 -0
  44. package/dist/types/skc-runtime/launch-tmux.d.ts +1 -0
  45. package/dist/types/skc-runtime/state-writer.d.ts +2 -0
  46. package/dist/types/skc-runtime/tmux-common.d.ts +3 -0
  47. package/dist/types/skc-runtime/tmux-sessions.d.ts +2 -0
  48. package/dist/types/skc-runtime/ultragoal-guard.d.ts +15 -0
  49. package/dist/types/skc-runtime/ultragoal-runtime.d.ts +14 -0
  50. package/dist/types/tools/ask-answer-registry.d.ts +13 -0
  51. package/dist/types/tools/index.d.ts +18 -0
  52. package/dist/types/tools/subagent.d.ts +3 -0
  53. package/package.json +7 -7
  54. package/scripts/build-binary.ts +3 -0
  55. package/src/async/job-manager.ts +5 -1
  56. package/src/cli/daemon-cli.ts +122 -0
  57. package/src/cli/notify-cli.ts +274 -0
  58. package/src/cli/setup-cli.ts +173 -84
  59. package/src/cli.ts +3 -3
  60. package/src/commands/daemon.ts +47 -0
  61. package/src/commands/notify.ts +61 -0
  62. package/src/commands/setup.ts +11 -1
  63. package/src/config/model-profile-activation.ts +74 -5
  64. package/src/config/model-profiles.ts +7 -4
  65. package/src/config/model-registry.ts +6 -3
  66. package/src/config/models-config-schema.ts +1 -1
  67. package/src/config/settings-schema.ts +29 -0
  68. package/src/coordinator/contract.ts +3 -0
  69. package/src/coordinator-mcp/server.ts +270 -1
  70. package/src/daemon/builtin.ts +46 -0
  71. package/src/daemon/control-types.ts +65 -0
  72. package/src/daemon/runtime.ts +51 -0
  73. package/src/defaults/skc/rules/ponytail.md +68 -0
  74. package/src/defaults/skc/skills/ultragoal/SKILL.md +16 -0
  75. package/src/defaults/skc-defaults.ts +27 -2
  76. package/src/edit/modes/replace.ts +1 -1
  77. package/src/extensibility/extensions/runner.ts +4 -0
  78. package/src/extensibility/extensions/types.ts +8 -0
  79. package/src/goals/tools/goal-tool.ts +11 -2
  80. package/src/hashline/hash.ts +1 -1
  81. package/src/internal-urls/docs-index.generated.ts +10 -8
  82. package/src/main.ts +30 -0
  83. package/src/modes/acp/acp-event-mapper.ts +1 -0
  84. package/src/modes/components/hook-editor.ts +7 -2
  85. package/src/modes/components/oauth-selector.ts +19 -0
  86. package/src/modes/controllers/event-controller.ts +20 -0
  87. package/src/modes/controllers/selector-controller.ts +80 -17
  88. package/src/modes/interactive-mode.ts +6 -2
  89. package/src/modes/runtime-init.ts +1 -0
  90. package/src/modes/shared/agent-wire/event-contract.ts +1 -0
  91. package/src/modes/shared/agent-wire/event-envelope.ts +1 -0
  92. package/src/modes/shared/agent-wire/event-observation.ts +16 -0
  93. package/src/modes/shared/agent-wire/unattended-session.ts +22 -0
  94. package/src/modes/types.ts +7 -1
  95. package/src/modes/utils/ui-helpers.ts +23 -0
  96. package/src/notifications/config-commands.ts +50 -0
  97. package/src/notifications/config.ts +107 -0
  98. package/src/notifications/helpers.ts +135 -0
  99. package/src/notifications/html-format.ts +389 -0
  100. package/src/notifications/index.ts +700 -0
  101. package/src/notifications/rate-limit-pool.ts +179 -0
  102. package/src/notifications/telegram-cli.ts +194 -0
  103. package/src/notifications/telegram-daemon-cli.ts +74 -0
  104. package/src/notifications/telegram-daemon-control.ts +370 -0
  105. package/src/notifications/telegram-daemon.ts +1370 -0
  106. package/src/notifications/telegram-reference.ts +335 -0
  107. package/src/notifications/threaded-inbound.ts +80 -0
  108. package/src/notifications/threaded-render.ts +155 -0
  109. package/src/notifications/topic-registry.ts +133 -0
  110. package/src/rlm/index.ts +19 -0
  111. package/src/sdk.ts +16 -0
  112. package/src/session/agent-session.ts +113 -3
  113. package/src/session/auth-storage.ts +3 -0
  114. package/src/session/session-dump-format.ts +43 -2
  115. package/src/session/session-manager.ts +39 -5
  116. package/src/setup/credential-auto-import.ts +258 -0
  117. package/src/setup/credential-import.ts +17 -0
  118. package/src/setup/hermes/templates/operator-instructions.v1.md +10 -0
  119. package/src/setup/host-plugin-setup.ts +142 -0
  120. package/src/skc-runtime/deep-interview-recorder.ts +2 -2
  121. package/src/skc-runtime/launch-tmux.ts +10 -2
  122. package/src/skc-runtime/state-runtime.ts +18 -10
  123. package/src/skc-runtime/state-writer.ts +8 -8
  124. package/src/skc-runtime/tmux-common.ts +8 -0
  125. package/src/skc-runtime/tmux-sessions.ts +8 -1
  126. package/src/skc-runtime/ultragoal-guard.ts +57 -2
  127. package/src/skc-runtime/ultragoal-runtime.ts +105 -19
  128. package/src/skc-runtime/workflow-manifest.generated.json +27 -2
  129. package/src/skc-runtime/workflow-manifest.ts +11 -1
  130. package/src/slash-commands/builtin-registry.ts +4 -1
  131. package/src/task/executor.ts +5 -1
  132. package/src/tools/ask-answer-registry.ts +25 -0
  133. package/src/tools/ask.ts +77 -6
  134. package/src/tools/image-gen.ts +5 -8
  135. package/src/tools/index.ts +19 -0
  136. package/src/tools/inspect-image.ts +16 -11
  137. package/src/tools/subagent-render.ts +7 -0
  138. package/src/tools/subagent.ts +38 -7
@@ -0,0 +1,63 @@
1
+ import type { AuthCredentialIfAbsentReason, AuthCredentialIfAbsentSnapshotResult, AuthStorage } from "@sayknow-cli/ai";
2
+ import type { ModelRegistry } from "../config/model-registry";
3
+ import { type CredentialDiscoveryResult, type CredentialOrigin, type DiscoveryOptions, type ImportableCredential } from "./credential-import";
4
+ export declare const CREDENTIAL_AUTO_IMPORT_ROTATION_WARNING = "Refreshing in skc may log out the Claude/Codex CLI because OAuth refresh tokens can rotate.";
5
+ export type CredentialAutoImportSourceLabel = "claude-code-file" | "claude-code-keychain" | "codex-file";
6
+ export type CredentialAutoImportTrigger = "startup" | "bare-login" | "setup-cli";
7
+ export declare function getCredentialAutoImportStatePath(agentDir?: string): string;
8
+ export declare function readCredentialImportMarker(agentDir?: string): Promise<string | undefined>;
9
+ export declare function writeCredentialImportMarker(version: string, agentDir?: string): Promise<boolean>;
10
+ export declare enum CredentialAutoImportFailureClass {
11
+ DiscoveryUnavailable = "discovery-unavailable",
12
+ SourceUnreadable = "source-unreadable",
13
+ SourceMalformed = "source-malformed",
14
+ KeychainDenied = "keychain-denied",
15
+ WriteInvalid = "write-invalid",
16
+ WriteConflict = "write-conflict",
17
+ BrokerUnavailable = "broker-unavailable",
18
+ BrokerUnsupported = "broker-unsupported",
19
+ Unknown = "unknown"
20
+ }
21
+ export interface CredentialAutoImportSkipped {
22
+ credential: ImportableCredential;
23
+ reason: AuthCredentialIfAbsentReason;
24
+ entries: AuthCredentialIfAbsentSnapshotResult["entries"];
25
+ }
26
+ export interface CredentialAutoImportFailure {
27
+ credential?: ImportableCredential;
28
+ origin?: CredentialOrigin;
29
+ source?: string;
30
+ failureClass: CredentialAutoImportFailureClass;
31
+ }
32
+ export interface CredentialAutoImportResult {
33
+ imported: ImportableCredential[];
34
+ skipped: CredentialAutoImportSkipped[];
35
+ failures: CredentialAutoImportFailure[];
36
+ discovered: boolean;
37
+ discovery?: CredentialDiscoveryResult;
38
+ globalDiscoveryFailure?: CredentialAutoImportFailure;
39
+ }
40
+ export type CredentialAutoImportAuthStorage = Pick<AuthStorage, "importCredentialIfAbsent">;
41
+ export interface CredentialAutoImportOptions {
42
+ authStorage: CredentialAutoImportAuthStorage;
43
+ discover?: (options?: DiscoveryOptions) => Promise<CredentialDiscoveryResult>;
44
+ discoveryOptions?: DiscoveryOptions;
45
+ trigger: CredentialAutoImportTrigger;
46
+ sourceLabel?: CredentialAutoImportSourceLabel;
47
+ }
48
+ export declare function runExternalCredentialAutoImport({ authStorage, discover, discoveryOptions }: CredentialAutoImportOptions): Promise<CredentialAutoImportResult>;
49
+ export declare function buildCredentialAutoImportNotice(result: Pick<CredentialAutoImportResult, "imported">): string | undefined;
50
+ export declare function formatCredentialAutoImportResult(result: CredentialAutoImportResult): string[];
51
+ export interface CredentialImportMarkerStore {
52
+ read: () => Promise<string | undefined> | string | undefined;
53
+ write: (version: string) => Promise<boolean> | boolean;
54
+ }
55
+ export interface StartupCredentialAutoImportOptions {
56
+ authStorage: CredentialAutoImportOptions["authStorage"];
57
+ modelRegistry: Pick<ModelRegistry, "refresh">;
58
+ discover?: CredentialAutoImportOptions["discover"];
59
+ version?: string;
60
+ agentDir?: string;
61
+ markerStore?: CredentialImportMarkerStore;
62
+ }
63
+ export declare function runStartupCredentialAutoImportIfNeeded({ authStorage: activeAuthStorage, modelRegistry: activeModelRegistry, discover, version, agentDir, markerStore }: StartupCredentialAutoImportOptions): Promise<string | undefined>;
@@ -3,6 +3,7 @@ import type { AuthCredential } from "@sayknow-cli/ai";
3
3
  export type ExternalProvider = "anthropic" | "openai-codex";
4
4
  /** Where a discovered credential came from. */
5
5
  export type CredentialOrigin = "claude-code-file" | "claude-code-keychain" | "codex-file";
6
+ export declare const AUTO_IMPORT_OAUTH_PROVIDER_ORIGINS: Record<ExternalProvider, ReadonlySet<CredentialOrigin>>;
6
7
  /** Human labels for providers, used in redacted summaries. */
7
8
  export declare const EXTERNAL_PROVIDER_LABELS: Record<ExternalProvider, string>;
8
9
  /** A credential that can be safely imported into skc's store. */
@@ -72,6 +73,8 @@ export declare function discoverExternalCredentials(options?: DiscoveryOptions):
72
73
  export declare function formatCredentialSummary(credential: ImportableCredential): string;
73
74
  /** Redacted summary lines for an entire discovery result. Never includes secrets. */
74
75
  export declare function formatDiscoverySummary(result: CredentialDiscoveryResult): string[];
76
+ export declare function isAutoImportOAuthCredential(credential: ImportableCredential): boolean;
77
+ export declare function filterAutoImportOAuthCredentials(credentials: readonly ImportableCredential[]): ImportableCredential[];
75
78
  /**
76
79
  * Persist discovered credentials via `upsert`. Each credential is imported
77
80
  * independently; a failure on one is recorded without aborting the rest.
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Host plugin setup for `skc setup claude` and `skc setup codex`.
3
+ *
4
+ * Renders install guidance and a fail-closed coordinator MCP config preview for
5
+ * the canonical generated plugin bundle under `plugins/`. This is intentionally
6
+ * render-only and fail-closed: the workdir allowlist is scoped to the project
7
+ * root and no mutation class is enabled until the user opts in.
8
+ */
9
+ export type HostPluginKind = "claude" | "codex";
10
+ export interface HostPluginSetupFlags {
11
+ json?: boolean;
12
+ check?: boolean;
13
+ root?: string[];
14
+ repo?: string;
15
+ }
16
+ export interface HostPluginSetupResult {
17
+ ok: true;
18
+ host: HostPluginKind;
19
+ mode: "render";
20
+ gated: boolean;
21
+ pluginPath: string;
22
+ manifestPath: string;
23
+ marketplacePath: string;
24
+ installGuidance: string[];
25
+ coordinatorConfigPreview: {
26
+ command: string;
27
+ args: string[];
28
+ env: Record<string, string>;
29
+ };
30
+ mutationPolicy: string;
31
+ notes: string[];
32
+ check?: {
33
+ ok: boolean;
34
+ checked: string[];
35
+ missing: string[];
36
+ };
37
+ }
38
+ export declare function buildHostPluginSetup(host: HostPluginKind, flags?: HostPluginSetupFlags): HostPluginSetupResult;
39
+ export declare function formatHostPluginSetup(result: HostPluginSetupResult): string;
@@ -69,6 +69,7 @@ export interface SkcTmuxProfileContext {
69
69
  project?: string | null;
70
70
  sessionId?: string | null;
71
71
  sessionStateFile?: string | null;
72
+ version?: string | null;
72
73
  }
73
74
  export declare function applySkcTmuxProfile(context: SkcTmuxProfileContext): SkcTmuxProfileResult;
74
75
  export declare function buildSkcTmuxWindowTitle(cwd: string, branch: string | null | undefined): string;
@@ -66,10 +66,12 @@ export interface GuardedStateWriterOptions extends StateWriterOptions {
66
66
  export type GuardedWriteResult = {
67
67
  path: string;
68
68
  written: true;
69
+ revision: number;
69
70
  } | {
70
71
  path: string;
71
72
  written: false;
72
73
  reason: "stale-skip";
74
+ revision: number;
73
75
  };
74
76
  export interface StateWriterOptions {
75
77
  cwd?: string;
@@ -10,6 +10,7 @@ export declare const SKC_TMUX_BRANCH_SLUG_OPTION = "@skc-branch-slug";
10
10
  export declare const SKC_TMUX_PROJECT_OPTION = "@skc-project";
11
11
  export declare const SKC_TMUX_SESSION_ID_OPTION = "@skc-session-id";
12
12
  export declare const SKC_TMUX_SESSION_STATE_FILE_OPTION = "@skc-session-state-file";
13
+ export declare const SKC_TMUX_VERSION_OPTION = "@skc-version";
13
14
  export interface SkcTmuxProfileCommand {
14
15
  description: string;
15
16
  args: string[];
@@ -50,6 +51,7 @@ export declare function buildSkcTmuxRequiredProfileCommands(target: string, meta
50
51
  project?: string | null;
51
52
  sessionId?: string | null;
52
53
  sessionStateFile?: string | null;
54
+ version?: string | null;
53
55
  }): SkcTmuxProfileCommand[];
54
56
  export declare function buildSkcTmuxProfileCommands(target: string, env?: NodeJS.ProcessEnv, metadata?: {
55
57
  branch?: string | null;
@@ -57,5 +59,6 @@ export declare function buildSkcTmuxProfileCommands(target: string, env?: NodeJS
57
59
  project?: string | null;
58
60
  sessionId?: string | null;
59
61
  sessionStateFile?: string | null;
62
+ version?: string | null;
60
63
  }): SkcTmuxProfileCommand[];
61
64
  export declare function normalizeTmuxCreatedAt(raw: string): string;
@@ -10,6 +10,7 @@ export interface SkcTmuxSessionStatus {
10
10
  project?: string;
11
11
  sessionId?: string;
12
12
  sessionStateFile?: string;
13
+ version?: string;
13
14
  panePids: number[];
14
15
  profile?: string;
15
16
  }
@@ -20,6 +21,7 @@ export interface SkcTmuxSessionTagsForGc {
20
21
  branchSlug?: string;
21
22
  sessionId?: string;
22
23
  sessionStateFile?: string;
24
+ version?: string;
23
25
  createdAt?: string;
24
26
  attached?: boolean;
25
27
  panePids?: number[];
@@ -27,10 +27,25 @@ export declare function validateCompletionReceipt(input: {
27
27
  export declare function readUltragoalVerificationState(input: {
28
28
  cwd: string;
29
29
  currentGoal?: CurrentGoalLike | null;
30
+ sessionId?: string | null;
30
31
  }): Promise<UltragoalGuardDiagnostic>;
31
32
  export declare function isUltragoalAskBlocked(cwd: string): Promise<UltragoalAskBlockDiagnostic>;
32
33
  export declare function assertCanCompleteCurrentGoal(input: {
33
34
  cwd: string;
34
35
  currentGoal?: CurrentGoalLike | null;
36
+ sessionId?: string | null;
35
37
  }): Promise<void>;
36
38
  export declare function isUltragoalBypassPrompt(prompt: string): boolean;
39
+ export interface UltragoalPauseBlockDiagnostic {
40
+ blocked: boolean;
41
+ reason: string;
42
+ }
43
+ /**
44
+ * While an Ultragoal run is active, `goal({"op":"pause"})` is only allowed when the
45
+ * current durable Ultragoal state is readable and the latest durable ledger event
46
+ * classifies the current blocker as `human_blocked`. Resolvable blockers must be
47
+ * worked, not parked. Reads fail closed so unreadable durable state or ledger data
48
+ * blocks pause rather than silently allowing a give-up.
49
+ */
50
+ export declare function isUltragoalPauseBlocked(cwd: string): Promise<UltragoalPauseBlockDiagnostic>;
51
+ export declare function assertUltragoalPauseAllowed(cwd: string): Promise<void>;
@@ -193,6 +193,19 @@ export declare function recordUltragoalReviewBlockers(input: {
193
193
  evidence: string;
194
194
  skcGoalJson?: string;
195
195
  }): Promise<UltragoalPlan>;
196
+ export type UltragoalBlockerClassification = "human_blocked" | "resolvable";
197
+ /**
198
+ * Record an audited blocker triage classification in the durable ledger. A
199
+ * `human_blocked` classification is the only thing that authorizes
200
+ * `goal({"op":"pause"})` while an Ultragoal run is active; `resolvable` is an
201
+ * audit note and never unblocks pause.
202
+ */
203
+ export declare function recordUltragoalBlockerClassification(input: {
204
+ cwd: string;
205
+ classification: UltragoalBlockerClassification;
206
+ evidence: string;
207
+ goalId?: string;
208
+ }): Promise<UltragoalLedgerEvent>;
196
209
  type UltragoalReviewContractStrength = "strong" | "thin-derived";
197
210
  interface UltragoalReviewFinding extends JsonObject {
198
211
  severity: "blocker";
@@ -208,6 +221,7 @@ interface UltragoalReviewResult extends JsonObject {
208
221
  weakContractCapApplied: boolean;
209
222
  blockerGoalIds?: string[];
210
223
  }
224
+ export declare function resolveGitBase(cwd: string, branch?: string): Promise<string>;
211
225
  export declare function runUltragoalReview(cwd: string, args: readonly string[]): Promise<UltragoalReviewResult>;
212
226
  export declare function runNativeUltragoalCommand(args: string[], cwd?: string): Promise<UltragoalCommandResult>;
213
227
  export {};
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Process-wide registry mapping a session id to its active {@link AskAnswerSource}.
3
+ *
4
+ * Decouples the `ask` tool (which reads the source via `AgentSession`) from the
5
+ * notifications extension (which registers one), without threading a new method
6
+ * through the extension/runner/controller wiring. A session has at most one
7
+ * source; registering returns a disposer.
8
+ */
9
+ import type { AskAnswerSource } from "./index";
10
+ /** Register `source` for `sessionId`. Returns a disposer that clears it. */
11
+ export declare function registerAskAnswerSource(sessionId: string, source: AskAnswerSource): () => void;
12
+ /** The answer source for `sessionId`, if one is registered. */
13
+ export declare function getAskAnswerSource(sessionId: string): AskAnswerSource | undefined;
@@ -68,6 +68,17 @@ export type ContextFileEntry = {
68
68
  };
69
69
  export type { DiscoverableMCPTool } from "../runtime-mcp/discoverable-tool-metadata";
70
70
  export type { DiscoverableTool, DiscoverableToolSearchIndex, DiscoverableToolSearchResult, DiscoverableToolSource, } from "../tool-discovery/tool-index";
71
+ /**
72
+ * Source of remote answers for interactive asks (e.g. a Telegram reply routed
73
+ * through the notifications SDK). Lets a pending ask resolve without RPC mode.
74
+ */
75
+ export interface AskAnswerSource {
76
+ /**
77
+ * Race a remote answer against the local UI for one question. Resolves with the
78
+ * chosen option label or free-text answer, or `undefined` to defer to local UI.
79
+ */
80
+ awaitAnswer(question: string, options: string[], signal?: AbortSignal): Promise<string | undefined>;
81
+ }
71
82
  /** Session context for tool factories */
72
83
  export interface ToolSession {
73
84
  /** Current working directory */
@@ -167,6 +178,13 @@ export interface ToolSession {
167
178
  getGoalModeState?: () => GoalModeState | undefined;
168
179
  /** Unattended workflow-gate emitter (present only when unattended mode is negotiated). */
169
180
  getWorkflowGateEmitter?: () => WorkflowGateEmitter | undefined;
181
+ /**
182
+ * Optional remote answer source for interactive asks. When present, the ask
183
+ * tool races the local UI selection against a remote answer (e.g. a Telegram
184
+ * reply via the notifications SDK) so asks can be answered without RPC mode.
185
+ * No-op when undefined: the ask path behaves exactly as before.
186
+ */
187
+ getAskAnswerSource?: () => AskAnswerSource | undefined;
170
188
  /** Optional per-session restriction for goal tool operations. */
171
189
  goalToolAllowedOps?: readonly ("create" | "get" | "complete" | "resume" | "drop" | "pause")[];
172
190
  /** Goal runtime for the active agent session. */
@@ -42,6 +42,9 @@ export interface SubagentSnapshot {
42
42
  outputRef?: string;
43
43
  truncated?: boolean;
44
44
  guidance?: string;
45
+ steerMessage?: string;
46
+ steerState?: "queued" | "resume_queued" | "resume_started";
47
+ steerPauseRequested?: boolean;
45
48
  /** Live streaming progress for the awaited subagent (await panel only; UI detail). */
46
49
  progress?: AgentProgress;
47
50
  /** True when a live in-session progress producer exists for this subagent. */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@sayknow-cli/coding-agent",
4
- "version": "0.2.7",
4
+ "version": "0.3.0",
5
5
  "description": "Sayknow-CLI CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/jaybeyond/Sayknow_CLI",
7
7
  "author": "jaybeyond",
@@ -51,12 +51,12 @@
51
51
  "@agentclientprotocol/sdk": "0.21.0",
52
52
  "@babel/parser": "^7.29.3",
53
53
  "@mozilla/readability": "^0.6.0",
54
- "@sayknow-cli/stats": "0.2.7",
55
- "@sayknow-cli/agent-core": "0.2.7",
56
- "@sayknow-cli/ai": "0.2.7",
57
- "@sayknow-cli/natives": "0.2.7",
58
- "@sayknow-cli/tui": "0.2.7",
59
- "@sayknow-cli/utils": "0.2.7",
54
+ "@sayknow-cli/stats": "0.3.0",
55
+ "@sayknow-cli/agent-core": "0.3.0",
56
+ "@sayknow-cli/ai": "0.3.0",
57
+ "@sayknow-cli/natives": "0.3.0",
58
+ "@sayknow-cli/tui": "0.3.0",
59
+ "@sayknow-cli/utils": "0.3.0",
60
60
  "@puppeteer/browsers": "^2.13.0",
61
61
  "@types/turndown": "5.0.6",
62
62
  "@xterm/headless": "^6.0.0",
@@ -66,6 +66,9 @@ async function main(): Promise<void> {
66
66
  "../stats/src/sync-worker.ts",
67
67
  "./src/tools/browser/tab-worker-entry.ts",
68
68
  "./src/eval/js/worker-entry.ts",
69
+ // Hidden notify daemon CLI (loaded via dynamic import from notify-cli); list it
70
+ // explicitly so Bun standalone keeps `skc notify daemon-internal` usable.
71
+ "./src/notifications/telegram-daemon-cli.ts",
69
72
  "--outfile",
70
73
  "dist/skc",
71
74
  ],
@@ -84,7 +84,11 @@ export interface SubagentLiveHandle {
84
84
  /** Request a cooperative safe-boundary pause (never aborts the in-flight tool). */
85
85
  requestPause(): void;
86
86
  /** Inject a steering message into the live session. */
87
- injectMessage(content: string, deliverAs: "steer" | "followUp" | "nextTurn"): Promise<void>;
87
+ injectMessage(
88
+ content: string,
89
+ deliverAs: "steer" | "followUp" | "nextTurn",
90
+ opts?: { fromAgentId?: string },
91
+ ): Promise<void>;
88
92
  }
89
93
 
90
94
  /**
@@ -0,0 +1,122 @@
1
+ /**
2
+ * `skc daemon` command handler.
3
+ *
4
+ * Generic over the static built-in daemon controller map: lists/inspects
5
+ * daemons and drives cooperative stop/reload. Telegram is the only kind today.
6
+ */
7
+
8
+ import { Settings } from "../config/settings";
9
+ import { selectDaemonControllers } from "../daemon/builtin";
10
+ import type {
11
+ BuiltInDaemonController,
12
+ DaemonKind,
13
+ DaemonOperationOptions,
14
+ DaemonOperationResult,
15
+ DaemonStatus,
16
+ } from "../daemon/control-types";
17
+
18
+ export type DaemonCliAction = "list" | "status" | "stop" | "reload";
19
+
20
+ export interface DaemonCommandArgs {
21
+ action: DaemonCliAction;
22
+ kinds: DaemonKind[];
23
+ all: boolean;
24
+ json: boolean;
25
+ force: boolean;
26
+ gracefulTimeoutMs?: number;
27
+ killTimeoutMs?: number;
28
+ spawnIfStopped?: boolean;
29
+ }
30
+
31
+ export interface DaemonCommandDeps {
32
+ settings?: Settings;
33
+ controllers?: BuiltInDaemonController[];
34
+ }
35
+
36
+ const KNOWN_ACTIONS: DaemonCliAction[] = ["list", "status", "stop", "reload"];
37
+ const KNOWN_KINDS: DaemonKind[] = ["telegram"];
38
+
39
+ export function parseDaemonArgs(argv: string[]): DaemonCommandArgs | undefined {
40
+ if (argv.length === 0 || argv[0] !== "daemon") return undefined;
41
+ const rest = argv.slice(1);
42
+ const action = (KNOWN_ACTIONS as string[]).includes(rest[0] ?? "") ? (rest[0] as DaemonCliAction) : "status";
43
+ const positional = (KNOWN_ACTIONS as string[]).includes(rest[0] ?? "") ? rest.slice(1) : rest;
44
+ const kinds: DaemonKind[] = [];
45
+ let all = false;
46
+ let json = false;
47
+ let force = false;
48
+ let gracefulTimeoutMs: number | undefined;
49
+ let killTimeoutMs: number | undefined;
50
+ let spawnIfStopped: boolean | undefined;
51
+ for (let i = 0; i < positional.length; i++) {
52
+ const arg = positional[i];
53
+ if (arg === "--all") all = true;
54
+ else if (arg === "--json") json = true;
55
+ else if (arg === "--force") force = true;
56
+ else if (arg === "--spawn-if-stopped") spawnIfStopped = true;
57
+ else if (arg === "--graceful-timeout-ms") gracefulTimeoutMs = Number.parseInt(positional[++i], 10);
58
+ else if (arg === "--kill-timeout-ms") killTimeoutMs = Number.parseInt(positional[++i], 10);
59
+ else if (!arg.startsWith("--") && (KNOWN_KINDS as string[]).includes(arg)) kinds.push(arg as DaemonKind);
60
+ }
61
+ return { action, kinds, all, json, force, gracefulTimeoutMs, killTimeoutMs, spawnIfStopped };
62
+ }
63
+
64
+ function formatStatus(status: DaemonStatus): string {
65
+ const parts = [
66
+ `${status.kind}: ${status.health}`,
67
+ status.configured ? undefined : "(not configured)",
68
+ status.pid !== undefined ? `pid=${status.pid}` : undefined,
69
+ status.ownerId ? `owner=${status.ownerId}` : undefined,
70
+ status.rootCount !== undefined ? `roots=${status.rootCount}` : undefined,
71
+ `mode=${status.runtime.mode}`,
72
+ ].filter(Boolean);
73
+ let line = parts.join(" ");
74
+ if (status.runtime.warning) line += `\n warning: ${status.runtime.warning}`;
75
+ return line;
76
+ }
77
+
78
+ function formatResult(result: DaemonOperationResult): string {
79
+ const head = `${result.kind} ${result.action}: ${result.ok ? "ok" : "failed"} — ${result.message}`;
80
+ const warnings = result.warnings.map(w => `\n warning: ${w}`).join("");
81
+ return head + warnings;
82
+ }
83
+
84
+ export async function runDaemonCommand(cmd: DaemonCommandArgs, deps: DaemonCommandDeps = {}): Promise<void> {
85
+ const unknownKinds = cmd.kinds.filter(kind => !(KNOWN_KINDS as string[]).includes(kind));
86
+ if (unknownKinds.length > 0) {
87
+ process.stderr.write(
88
+ `Unknown daemon kind(s): ${unknownKinds.join(", ")}. Known kinds: ${KNOWN_KINDS.join(", ")}.\n`,
89
+ );
90
+ process.exitCode = 1;
91
+ return;
92
+ }
93
+ const settings = deps.settings ?? (await Settings.init());
94
+ const controllers = deps.controllers ?? selectDaemonControllers(settings, cmd.kinds, cmd.all);
95
+
96
+ if (cmd.action === "list" || cmd.action === "status") {
97
+ const statuses = await Promise.all(controllers.map(c => c.status()));
98
+ if (cmd.json) {
99
+ process.stdout.write(`${JSON.stringify(statuses, null, 2)}\n`);
100
+ } else {
101
+ process.stdout.write(`${statuses.map(formatStatus).join("\n")}\n`);
102
+ }
103
+ return;
104
+ }
105
+
106
+ const opts: DaemonOperationOptions = {
107
+ gracefulTimeoutMs: cmd.gracefulTimeoutMs,
108
+ killTimeoutMs: cmd.killTimeoutMs,
109
+ force: cmd.force,
110
+ spawnIfStopped: cmd.spawnIfStopped,
111
+ };
112
+ const results: DaemonOperationResult[] = [];
113
+ for (const controller of controllers) {
114
+ results.push(cmd.action === "reload" ? await controller.reload(opts) : await controller.stop(opts));
115
+ }
116
+ if (cmd.json) {
117
+ process.stdout.write(`${JSON.stringify(results, null, 2)}\n`);
118
+ } else {
119
+ process.stdout.write(`${results.map(formatResult).join("\n")}\n`);
120
+ }
121
+ if (results.some(r => !r.ok)) process.exitCode = 1;
122
+ }