@gajae-code/coding-agent 0.4.5 → 0.5.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 (87) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/types/commands/harness.d.ts +3 -0
  3. package/dist/types/config/model-profile-activation.d.ts +11 -2
  4. package/dist/types/config/model-profiles.d.ts +7 -0
  5. package/dist/types/config/model-registry.d.ts +3 -0
  6. package/dist/types/config/model-resolver.d.ts +2 -0
  7. package/dist/types/config/models-config-schema.d.ts +30 -0
  8. package/dist/types/config/settings-schema.d.ts +4 -3
  9. package/dist/types/gjc-runtime/team-runtime.d.ts +0 -1
  10. package/dist/types/gjc-runtime/tmux-common.d.ts +3 -0
  11. package/dist/types/harness-control-plane/owner.d.ts +1 -1
  12. package/dist/types/harness-control-plane/receipt-spool.d.ts +19 -0
  13. package/dist/types/harness-control-plane/state-machine.d.ts +6 -1
  14. package/dist/types/harness-control-plane/types.d.ts +4 -0
  15. package/dist/types/hindsight/mental-models.d.ts +5 -5
  16. package/dist/types/modes/components/model-selector.d.ts +1 -12
  17. package/dist/types/modes/rpc/rpc-client.d.ts +2 -2
  18. package/dist/types/modes/rpc/rpc-types.d.ts +4 -1
  19. package/dist/types/sdk.d.ts +5 -0
  20. package/dist/types/session/agent-session.d.ts +2 -0
  21. package/dist/types/session/blob-store.d.ts +20 -1
  22. package/dist/types/session/session-manager.d.ts +24 -6
  23. package/dist/types/session/streaming-output.d.ts +3 -2
  24. package/dist/types/session/tool-choice-queue.d.ts +6 -0
  25. package/dist/types/task/receipt.d.ts +1 -0
  26. package/dist/types/task/types.d.ts +7 -0
  27. package/dist/types/thinking-metadata.d.ts +16 -0
  28. package/dist/types/thinking.d.ts +3 -12
  29. package/dist/types/tools/index.d.ts +2 -0
  30. package/dist/types/tools/resolve.d.ts +0 -10
  31. package/dist/types/utils/tool-choice.d.ts +14 -1
  32. package/package.json +7 -7
  33. package/src/cli.ts +8 -4
  34. package/src/commands/harness.ts +36 -2
  35. package/src/commands/launch.ts +2 -2
  36. package/src/commands/session.ts +3 -1
  37. package/src/config/model-profile-activation.ts +15 -3
  38. package/src/config/model-profiles.ts +255 -56
  39. package/src/config/model-resolver.ts +9 -6
  40. package/src/config/models-config-schema.ts +1 -0
  41. package/src/config/settings-schema.ts +6 -3
  42. package/src/coordinator-mcp/server.ts +54 -23
  43. package/src/cursor.ts +16 -2
  44. package/src/defaults/gjc/skills/team/SKILL.md +3 -2
  45. package/src/defaults/gjc/skills/ultragoal/SKILL.md +8 -2
  46. package/src/export/html/index.ts +13 -9
  47. package/src/gjc-runtime/team-runtime.ts +33 -7
  48. package/src/gjc-runtime/tmux-common.ts +15 -0
  49. package/src/gjc-runtime/tmux-sessions.ts +19 -11
  50. package/src/gjc-runtime/ultragoal-runtime.ts +505 -41
  51. package/src/gjc-runtime/workflow-manifest.generated.json +27 -1
  52. package/src/gjc-runtime/workflow-manifest.ts +16 -1
  53. package/src/harness-control-plane/owner.ts +78 -27
  54. package/src/harness-control-plane/receipt-spool.ts +128 -0
  55. package/src/harness-control-plane/state-machine.ts +27 -6
  56. package/src/harness-control-plane/storage.ts +23 -0
  57. package/src/harness-control-plane/types.ts +4 -0
  58. package/src/hindsight/mental-models.ts +17 -16
  59. package/src/internal-urls/docs-index.generated.ts +2 -2
  60. package/src/modes/components/assistant-message.ts +26 -14
  61. package/src/modes/components/diff.ts +97 -0
  62. package/src/modes/components/model-selector.ts +353 -181
  63. package/src/modes/components/tool-execution.ts +30 -13
  64. package/src/modes/controllers/selector-controller.ts +33 -42
  65. package/src/modes/rpc/rpc-client.ts +3 -2
  66. package/src/modes/rpc/rpc-mode.ts +44 -14
  67. package/src/modes/rpc/rpc-types.ts +5 -2
  68. package/src/modes/shared/agent-wire/command-dispatch.ts +10 -5
  69. package/src/modes/shared/agent-wire/command-validation.ts +11 -0
  70. package/src/sdk.ts +29 -2
  71. package/src/secrets/obfuscator.ts +102 -27
  72. package/src/session/agent-session.ts +105 -20
  73. package/src/session/blob-store.ts +89 -3
  74. package/src/session/session-manager.ts +309 -58
  75. package/src/session/streaming-output.ts +185 -122
  76. package/src/session/tool-choice-queue.ts +23 -0
  77. package/src/task/executor.ts +69 -6
  78. package/src/task/receipt.ts +5 -0
  79. package/src/task/render.ts +21 -1
  80. package/src/task/types.ts +8 -0
  81. package/src/thinking-metadata.ts +51 -0
  82. package/src/thinking.ts +26 -46
  83. package/src/tools/bash.ts +1 -1
  84. package/src/tools/index.ts +2 -0
  85. package/src/tools/resolve.ts +93 -18
  86. package/src/utils/edit-mode.ts +1 -1
  87. package/src/utils/tool-choice.ts +45 -16
@@ -5,7 +5,7 @@ import type { WorkflowHudSummary } from "../skill-state/active-state";
5
5
  import { buildTeamHudSummary as buildWorkflowTeamHudSummary } from "../skill-state/workflow-hud";
6
6
  import { WORKFLOW_STATE_VERSION } from "../skill-state/workflow-state-contract";
7
7
 
8
- import { applyGjcTmuxProfile } from "./launch-tmux";
8
+ import { applyGjcTmuxProfile, GJC_TMUX_LAUNCHED_ENV } from "./launch-tmux";
9
9
  import {
10
10
  AlreadyExistsError,
11
11
  appendJsonl as appendJsonlAudited,
@@ -17,7 +17,12 @@ import {
17
17
  writeReport,
18
18
  writeWorkflowEnvelopeAtomic,
19
19
  } from "./state-writer";
20
- import { GJC_TMUX_PROFILE_OPTION, GJC_TMUX_PROFILE_VALUE } from "./tmux-common";
20
+ import {
21
+ buildGjcTmuxUntaggedSessionHint,
22
+ GJC_TMUX_PROFILE_OPTION,
23
+ GJC_TMUX_PROFILE_VALUE,
24
+ resolveGjcTmuxCommand,
25
+ } from "./tmux-common";
21
26
 
22
27
  export type GjcTeamPhase = "starting" | "running" | "awaiting_integration" | "complete" | "failed" | "cancelled";
23
28
  export type GjcTeamTaskStatus = "pending" | "blocked" | "in_progress" | "completed" | "failed";
@@ -1622,9 +1627,6 @@ async function ensureWorkerWorktree(
1622
1627
  };
1623
1628
  }
1624
1629
 
1625
- export function resolveGjcTmuxCommand(env: NodeJS.ProcessEnv = process.env): string {
1626
- return env.GJC_TEAM_TMUX_COMMAND?.trim() || "tmux";
1627
- }
1628
1630
  function buildTeamTmuxLeaderRequirementMessage(detail?: string): string {
1629
1631
  const suffix = detail?.trim() ? `:${detail.trim()}` : "";
1630
1632
  return `gjc_team_requires_tmux_leader: run \`gjc --tmux\` first, then run \`gjc team ...\` inside that tmux-backed leader session, or use \`gjc team --dry-run\` for state-only smoke tests${suffix}`;
@@ -1641,6 +1643,17 @@ function readGjcTmuxProfileValue(tmuxCommand: string, sessionName: string): stri
1641
1643
  return result.stdout.toString().trim();
1642
1644
  }
1643
1645
 
1646
+ function retagGjcLaunchedTmuxSession(tmuxCommand: string, sessionName: string): boolean {
1647
+ const result = Bun.spawnSync(
1648
+ [tmuxCommand, "set-option", "-t", `=${sessionName}`, GJC_TMUX_PROFILE_OPTION, GJC_TMUX_PROFILE_VALUE],
1649
+ {
1650
+ stdout: "pipe",
1651
+ stderr: "pipe",
1652
+ },
1653
+ );
1654
+ return result.exitCode === 0;
1655
+ }
1656
+
1644
1657
  function readCurrentTmuxLeaderContext(tmuxCommand: string, env: NodeJS.ProcessEnv): GjcTmuxLeaderContext {
1645
1658
  const paneTarget = env.TMUX_PANE?.trim();
1646
1659
  const args = paneTarget
@@ -1652,8 +1665,21 @@ function readCurrentTmuxLeaderContext(tmuxCommand: string, env: NodeJS.ProcessEn
1652
1665
  const [sessionName = "", windowIndex = ""] = sessionAndWindow.split(":");
1653
1666
  if (!sessionName || !windowIndex || !leaderPaneId.startsWith("%"))
1654
1667
  throw new Error(buildTeamTmuxLeaderRequirementMessage(`invalid_tmux_context:${result.stdout.toString().trim()}`));
1655
- if (readGjcTmuxProfileValue(tmuxCommand, sessionName) !== GJC_TMUX_PROFILE_VALUE)
1656
- throw new Error(buildTeamTmuxLeaderRequirementMessage(`unmanaged_tmux_session:${sessionName}`));
1668
+ if (readGjcTmuxProfileValue(tmuxCommand, sessionName) !== GJC_TMUX_PROFILE_VALUE) {
1669
+ // Self-heal: a pane launched through `gjc --tmux` exports
1670
+ // GJC_TMUX_LAUNCHED=1, but the session can lose (or never receive) the
1671
+ // @gjc-profile user-option tag when startup attach fails mid-way or the
1672
+ // registry write races. That stranded-but-genuinely-GJC leader pane
1673
+ // previously hard-failed as unmanaged_tmux_session; re-tag it instead.
1674
+ const launchedByGjc = env[GJC_TMUX_LAUNCHED_ENV] === "1";
1675
+ const retagged = launchedByGjc && retagGjcLaunchedTmuxSession(tmuxCommand, sessionName);
1676
+ if (!retagged || readGjcTmuxProfileValue(tmuxCommand, sessionName) !== GJC_TMUX_PROFILE_VALUE)
1677
+ throw new Error(
1678
+ buildTeamTmuxLeaderRequirementMessage(
1679
+ `unmanaged_tmux_session:${sessionName} — ${buildGjcTmuxUntaggedSessionHint(tmuxCommand)}`,
1680
+ ),
1681
+ );
1682
+ }
1657
1683
  return { sessionName, windowIndex, leaderPaneId, target: `${sessionName}:${windowIndex}` };
1658
1684
  }
1659
1685
  export function resolveGjcWorkerCommand(cwd = process.cwd(), env: NodeJS.ProcessEnv = process.env): string {
@@ -32,6 +32,21 @@ export function resolveGjcTmuxCommand(env: NodeJS.ProcessEnv = process.env): str
32
32
  return env[GJC_TMUX_COMMAND_ENV]?.trim() || env.GJC_TEAM_TMUX_COMMAND?.trim() || "tmux";
33
33
  }
34
34
 
35
+ export const GJC_TMUX_UNTAGGED_REASON = "gjc_tmux_session_untagged";
36
+
37
+ export function buildGjcTmuxUntaggedSessionHint(tmuxCommand: string): string {
38
+ return (
39
+ `the active multiplexer "${tmuxCommand}" lists this session but did not return GJC's ${GJC_TMUX_PROFILE_OPTION} ownership tag; ` +
40
+ "GJC-managed sessions and `gjc team` require a tmux provider that round-trips tmux user options. " +
41
+ "Alternative multiplexers such as psmux on Windows do not persist user options yet, so the Windows-native psmux path is not fully supported; " +
42
+ "use real tmux for GJC-managed session and team flows."
43
+ );
44
+ }
45
+
46
+ export function buildGjcTmuxUntaggedSessionError(sessionName: string, tmuxCommand: string): string {
47
+ return `${GJC_TMUX_UNTAGGED_REASON}:${sessionName} — ${buildGjcTmuxUntaggedSessionHint(tmuxCommand)}`;
48
+ }
49
+
35
50
  export function sanitizeTmuxToken(value: string): string {
36
51
  return (
37
52
  value
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  buildGjcTmuxProfileCommands,
3
3
  buildGjcTmuxSessionName,
4
+ buildGjcTmuxUntaggedSessionError,
4
5
  GJC_TMUX_BRANCH_OPTION,
5
6
  GJC_TMUX_BRANCH_SLUG_OPTION,
6
7
  GJC_TMUX_PROFILE_OPTION,
@@ -73,17 +74,10 @@ function parseSessionLine(line: string): GjcTmuxSessionStatus | null {
73
74
  };
74
75
  }
75
76
 
76
- function listSessionLines(env: NodeJS.ProcessEnv = process.env): string[] {
77
+ function runListSessions(format: string, env: NodeJS.ProcessEnv = process.env): string[] {
77
78
  let output = "";
78
79
  try {
79
- output = runTmux(
80
- [
81
- "list-sessions",
82
- "-F",
83
- `#{session_name}\t#{session_windows}\t#{session_attached}\t#{session_created}\t#{${GJC_TMUX_PROFILE_OPTION}}\t#{session_key_table}\t#{session_panes}\t#{${GJC_TMUX_BRANCH_OPTION}}\t#{${GJC_TMUX_BRANCH_SLUG_OPTION}}\t#{${GJC_TMUX_PROJECT_OPTION}}`,
84
- ],
85
- env,
86
- );
80
+ output = runTmux(["list-sessions", "-F", format], env);
87
81
  } catch (error) {
88
82
  const message = error instanceof Error ? error.message : String(error);
89
83
  if (message.includes("no server running") || message.includes("failed to connect to server")) return [];
@@ -95,6 +89,17 @@ function listSessionLines(env: NodeJS.ProcessEnv = process.env): string[] {
95
89
  .filter(Boolean);
96
90
  }
97
91
 
92
+ function listSessionLines(env: NodeJS.ProcessEnv = process.env): string[] {
93
+ return runListSessions(
94
+ `#{session_name}\t#{session_windows}\t#{session_attached}\t#{session_created}\t#{${GJC_TMUX_PROFILE_OPTION}}\t#{session_key_table}\t#{session_panes}\t#{${GJC_TMUX_BRANCH_OPTION}}\t#{${GJC_TMUX_BRANCH_SLUG_OPTION}}\t#{${GJC_TMUX_PROJECT_OPTION}}`,
95
+ env,
96
+ );
97
+ }
98
+
99
+ function listRawTmuxSessionNames(env: NodeJS.ProcessEnv = process.env): string[] {
100
+ return runListSessions("#{session_name}", env).map(line => line.split("\t")[0] ?? line);
101
+ }
102
+
98
103
  export function listGjcTmuxSessions(env: NodeJS.ProcessEnv = process.env): GjcTmuxSessionStatus[] {
99
104
  return listSessionLines(env)
100
105
  .map(parseSessionLine)
@@ -114,8 +119,11 @@ export function findGjcTmuxSessionByBranch(
114
119
 
115
120
  export function statusGjcTmuxSession(sessionName: string, env: NodeJS.ProcessEnv = process.env): GjcTmuxSessionStatus {
116
121
  const session = listGjcTmuxSessions(env).find(candidate => candidate.name === sessionName);
117
- if (!session) throw new Error(`gjc_tmux_session_not_found:${sessionName}`);
118
- return session;
122
+ if (session) return session;
123
+ if (listRawTmuxSessionNames(env).includes(sessionName)) {
124
+ throw new Error(buildGjcTmuxUntaggedSessionError(sessionName, resolveGjcTmuxCommand(env)));
125
+ }
126
+ throw new Error(`gjc_tmux_session_not_found:${sessionName}`);
119
127
  }
120
128
 
121
129
  export function createGjcTmuxSession(env: NodeJS.ProcessEnv = process.env): GjcTmuxSessionStatus {