@phamvuhoang/otto-core 0.18.0 → 0.20.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 (72) hide show
  1. package/dist/cli-help.d.ts +18 -0
  2. package/dist/cli-help.d.ts.map +1 -1
  3. package/dist/cli-help.js +32 -1
  4. package/dist/cli-help.js.map +1 -1
  5. package/dist/fanout.d.ts +56 -0
  6. package/dist/fanout.d.ts.map +1 -0
  7. package/dist/fanout.js +119 -0
  8. package/dist/fanout.js.map +1 -0
  9. package/dist/index.d.ts +10 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +10 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/journal-config.d.ts +9 -0
  14. package/dist/journal-config.d.ts.map +1 -0
  15. package/dist/journal-config.js +31 -0
  16. package/dist/journal-config.js.map +1 -0
  17. package/dist/journal-gate.d.ts +46 -0
  18. package/dist/journal-gate.d.ts.map +1 -0
  19. package/dist/journal-gate.js +135 -0
  20. package/dist/journal-gate.js.map +1 -0
  21. package/dist/journal-ledger.d.ts +13 -0
  22. package/dist/journal-ledger.d.ts.map +1 -0
  23. package/dist/journal-ledger.js +33 -0
  24. package/dist/journal-ledger.js.map +1 -0
  25. package/dist/journal-source.d.ts +19 -0
  26. package/dist/journal-source.d.ts.map +1 -0
  27. package/dist/journal-source.js +38 -0
  28. package/dist/journal-source.js.map +1 -0
  29. package/dist/journal.d.ts +46 -0
  30. package/dist/journal.d.ts.map +1 -0
  31. package/dist/journal.js +158 -0
  32. package/dist/journal.js.map +1 -0
  33. package/dist/loop.d.ts +13 -0
  34. package/dist/loop.d.ts.map +1 -1
  35. package/dist/loop.js +91 -7
  36. package/dist/loop.js.map +1 -1
  37. package/dist/model-tier.d.ts +69 -0
  38. package/dist/model-tier.d.ts.map +1 -0
  39. package/dist/model-tier.js +101 -0
  40. package/dist/model-tier.js.map +1 -0
  41. package/dist/plan-tasks.d.ts +44 -0
  42. package/dist/plan-tasks.d.ts.map +1 -0
  43. package/dist/plan-tasks.js +167 -0
  44. package/dist/plan-tasks.js.map +1 -0
  45. package/dist/run-bin.d.ts.map +1 -1
  46. package/dist/run-bin.js +21 -1
  47. package/dist/run-bin.js.map +1 -1
  48. package/dist/runner.d.ts +18 -14
  49. package/dist/runner.d.ts.map +1 -1
  50. package/dist/runner.js +12 -23
  51. package/dist/runner.js.map +1 -1
  52. package/dist/stage-exec.d.ts +13 -0
  53. package/dist/stage-exec.d.ts.map +1 -1
  54. package/dist/stage-exec.js +32 -1
  55. package/dist/stage-exec.js.map +1 -1
  56. package/dist/stages.d.ts +28 -0
  57. package/dist/stages.d.ts.map +1 -1
  58. package/dist/stages.js +30 -0
  59. package/dist/stages.js.map +1 -1
  60. package/dist/threads-api.d.ts +46 -0
  61. package/dist/threads-api.d.ts.map +1 -0
  62. package/dist/threads-api.js +101 -0
  63. package/dist/threads-api.js.map +1 -0
  64. package/dist/worktree.d.ts +24 -0
  65. package/dist/worktree.d.ts.map +1 -0
  66. package/dist/worktree.js +43 -0
  67. package/dist/worktree.js.map +1 -0
  68. package/package.json +1 -1
  69. package/templates/journal-screen.md +19 -0
  70. package/templates/journal-write.md +16 -0
  71. package/templates/plan.md +26 -4
  72. package/templates/subtask.md +32 -0
@@ -4,10 +4,18 @@ import { DEFAULT_AGENT } from "./agent-runtime.js";
4
4
  import { analyzeContext } from "./context-report.js";
5
5
  import { applyPromptReduction } from "./prompt-reduction.js";
6
6
  import { renderTemplate } from "./render.js";
7
+ import { resolveStageModel } from "./model-tier.js";
7
8
  import { DEFAULT_BACKOFF_MS, backoffFor, withRetries } from "./retry.js";
8
9
  import { getAgentRuntime, runStage, stageLogPath, } from "./runner.js";
9
10
  import { readSafetyPolicy, } from "./safety-policy.js";
10
11
  import { USE_COLOR, dim } from "./stream-render.js";
12
+ /** Fallback ladder when routing is requested without one — every tier resolves
13
+ * to the runtime default (issue #66 P11). The real ladder comes from run-bin. */
14
+ const EMPTY_LADDER = {
15
+ cheap: undefined,
16
+ mid: undefined,
17
+ strong: undefined,
18
+ };
11
19
  /** A policy violation found at a shell/@spill tag becomes a `blocked` trajectory
12
20
  * safety event — the command was denied and skipped, so Otto prevented it. */
13
21
  function violationToSafetyEvent(v) {
@@ -50,12 +58,35 @@ export async function executeStage(opts) {
50
58
  const { originalChars, reducedChars, cacheHits } = reduced.stats;
51
59
  process.stderr.write(`${dim(`prompt reduce ${originalChars} -> ${reducedChars} chars | cache hits ${cacheHits}`)}\n`);
52
60
  }
53
- const result = await runStage(stage, prompt, workspaceDir, iteration, spillHostDir, stageLog, { signal, runtime, sink: opts.sink });
61
+ // Route the model for this stage (issue #66 P11): a pin wins, else the
62
+ // declared tier through the ladder when routing is on, else runtime default.
63
+ const model = resolveStageModel({
64
+ runtimeId: opts.agentId ?? DEFAULT_AGENT,
65
+ stage,
66
+ routing: opts.modelRouting === true,
67
+ ladder: opts.tierLadder ?? EMPTY_LADDER,
68
+ assessment: opts.riskAssessment,
69
+ escalations: opts.escalations,
70
+ });
71
+ const result = await runStage(stage, prompt, workspaceDir, iteration, spillHostDir, stageLog, {
72
+ signal,
73
+ runtime,
74
+ sink: opts.sink,
75
+ modelSpec: model.spec,
76
+ sandboxWriteRoots: opts.sandboxWriteRoots,
77
+ });
54
78
  // Attribute the *final* prompt's window footprint by category (issue #62
55
79
  // P7). `prompt` is post-reduction, so the breakdown reflects what was sent.
56
80
  return {
57
81
  ...result,
58
82
  contextBreakdown: analyzeContext(prompt),
83
+ ...(model.tier
84
+ ? {
85
+ routedTier: model.tier,
86
+ routedModel: model.spec,
87
+ modelSource: model.source,
88
+ }
89
+ : {}),
59
90
  ...(violations.length > 0
60
91
  ? { safetyEvents: violations.map(violationToSafetyEvent) }
61
92
  : {}),
@@ -1 +1 @@
1
- {"version":3,"file":"stage-exec.js","sourceRoot":"","sources":["../src/stage-exec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"stage-exec.js","sourceRoot":"","sources":["../src/stage-exec.ts"],"names":[],"mappings":"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"}
package/dist/stages.d.ts CHANGED
@@ -1,53 +1,81 @@
1
+ import type { ModelTier } from "./model-tier.js";
1
2
  export type Stage = {
2
3
  name: string;
3
4
  template: string;
4
5
  permissionMode?: string;
6
+ /** Difficulty tier for model routing (issue #66 P11). Absent ⇒ runtime default model. */
7
+ tier?: ModelTier;
5
8
  };
6
9
  export declare const STAGES: {
7
10
  plan: {
8
11
  name: string;
9
12
  template: string;
10
13
  permissionMode: string;
14
+ tier: "strong";
11
15
  };
12
16
  implementer: {
13
17
  name: string;
14
18
  template: string;
15
19
  permissionMode: string;
20
+ tier: "mid";
16
21
  };
17
22
  ghafkImplementer: {
18
23
  name: string;
19
24
  template: string;
20
25
  permissionMode: string;
26
+ tier: "mid";
21
27
  };
22
28
  ghafkIssueImplementer: {
23
29
  name: string;
24
30
  template: string;
25
31
  permissionMode: string;
32
+ tier: "mid";
26
33
  };
27
34
  linearImplementer: {
28
35
  name: string;
29
36
  template: string;
30
37
  permissionMode: string;
38
+ tier: "mid";
31
39
  };
32
40
  linearIssueImplementer: {
33
41
  name: string;
34
42
  template: string;
35
43
  permissionMode: string;
44
+ tier: "mid";
36
45
  };
37
46
  verifier: {
38
47
  name: string;
39
48
  template: string;
40
49
  permissionMode: string;
50
+ tier: "strong";
41
51
  };
42
52
  applyReviewImplementer: {
43
53
  name: string;
44
54
  template: string;
45
55
  permissionMode: string;
56
+ tier: "strong";
46
57
  };
47
58
  reviewer: {
48
59
  name: string;
49
60
  template: string;
50
61
  permissionMode: string;
62
+ tier: "strong";
63
+ };
64
+ subImplementer: {
65
+ name: string;
66
+ template: string;
67
+ permissionMode: string;
68
+ tier: "mid";
69
+ };
70
+ journalWrite: {
71
+ name: string;
72
+ template: string;
73
+ permissionMode: string;
74
+ };
75
+ journalScreen: {
76
+ name: string;
77
+ template: string;
78
+ permissionMode: string;
51
79
  };
52
80
  };
53
81
  //# sourceMappingURL=stages.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stages.d.ts","sourceRoot":"","sources":["../src/stages.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,KAAK,GAAG;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAOF,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgDlB,CAAC"}
1
+ {"version":3,"file":"stages.d.ts","sourceRoot":"","sources":["../src/stages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,MAAM,KAAK,GAAG;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yFAAyF;IACzF,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB,CAAC;AAOF,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8ElB,CAAC"}
package/dist/stages.js CHANGED
@@ -10,46 +10,76 @@ export const STAGES = {
10
10
  name: "plan",
11
11
  template: "plan.md",
12
12
  permissionMode: "bypassPermissions",
13
+ tier: "strong",
13
14
  },
14
15
  implementer: {
15
16
  name: "implementer",
16
17
  template: "afk.md",
17
18
  permissionMode: "bypassPermissions",
19
+ tier: "mid",
18
20
  },
19
21
  ghafkImplementer: {
20
22
  name: "ghafk-implementer",
21
23
  template: "ghafk.md",
22
24
  permissionMode: "bypassPermissions",
25
+ tier: "mid",
23
26
  },
24
27
  ghafkIssueImplementer: {
25
28
  name: "ghafk-issue-implementer",
26
29
  template: "ghafk-issue.md",
27
30
  permissionMode: "bypassPermissions",
31
+ tier: "mid",
28
32
  },
29
33
  linearImplementer: {
30
34
  name: "linear-implementer",
31
35
  template: "linearafk.md",
32
36
  permissionMode: "bypassPermissions",
37
+ tier: "mid",
33
38
  },
34
39
  linearIssueImplementer: {
35
40
  name: "linear-issue-implementer",
36
41
  template: "linearafk-issue.md",
37
42
  permissionMode: "bypassPermissions",
43
+ tier: "mid",
38
44
  },
39
45
  verifier: {
40
46
  name: "verifier",
41
47
  template: "verify.md",
42
48
  permissionMode: "bypassPermissions",
49
+ tier: "strong",
43
50
  },
44
51
  applyReviewImplementer: {
45
52
  name: "apply-review-implementer",
46
53
  template: "apply-review.md",
47
54
  permissionMode: "bypassPermissions",
55
+ tier: "strong",
48
56
  },
49
57
  reviewer: {
50
58
  name: "reviewer",
51
59
  template: "review.md",
52
60
  permissionMode: "bypassPermissions",
61
+ tier: "strong",
62
+ },
63
+ // One fanned-out plan task, run in an isolated worktree with bounded context
64
+ // (issue #66 P11). Mechanical-to-moderate per-task work → mid tier.
65
+ subImplementer: {
66
+ name: "sub-implementer",
67
+ template: "subtask.md",
68
+ permissionMode: "bypassPermissions",
69
+ tier: "mid",
70
+ },
71
+ // P12 public journal (issue #67): generate a generic field note from a memory
72
+ // learning, and adversarially screen a candidate note for leaks. Both produce
73
+ // only text — the harness owns the secrecy gate and the actual posting.
74
+ journalWrite: {
75
+ name: "journal-write",
76
+ template: "journal-write.md",
77
+ permissionMode: "bypassPermissions",
78
+ },
79
+ journalScreen: {
80
+ name: "journal-screen",
81
+ template: "journal-screen.md",
82
+ permissionMode: "bypassPermissions",
53
83
  },
54
84
  };
55
85
  //# sourceMappingURL=stages.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stages.js","sourceRoot":"","sources":["../src/stages.ts"],"names":[],"mappings":"AAMA,gFAAgF;AAChF,8EAA8E;AAC9E,iFAAiF;AACjF,kFAAkF;AAClF,gFAAgF;AAChF,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,yEAAyE;IACzE,4EAA4E;IAC5E,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,SAAS;QACnB,cAAc,EAAE,mBAAmB;KACpB;IACjB,WAAW,EAAE;QACX,IAAI,EAAE,aAAa;QACnB,QAAQ,EAAE,QAAQ;QAClB,cAAc,EAAE,mBAAmB;KACpB;IACjB,gBAAgB,EAAE;QAChB,IAAI,EAAE,mBAAmB;QACzB,QAAQ,EAAE,UAAU;QACpB,cAAc,EAAE,mBAAmB;KACpB;IACjB,qBAAqB,EAAE;QACrB,IAAI,EAAE,yBAAyB;QAC/B,QAAQ,EAAE,gBAAgB;QAC1B,cAAc,EAAE,mBAAmB;KACpB;IACjB,iBAAiB,EAAE;QACjB,IAAI,EAAE,oBAAoB;QAC1B,QAAQ,EAAE,cAAc;QACxB,cAAc,EAAE,mBAAmB;KACpB;IACjB,sBAAsB,EAAE;QACtB,IAAI,EAAE,0BAA0B;QAChC,QAAQ,EAAE,oBAAoB;QAC9B,cAAc,EAAE,mBAAmB;KACpB;IACjB,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,QAAQ,EAAE,WAAW;QACrB,cAAc,EAAE,mBAAmB;KACpB;IACjB,sBAAsB,EAAE;QACtB,IAAI,EAAE,0BAA0B;QAChC,QAAQ,EAAE,iBAAiB;QAC3B,cAAc,EAAE,mBAAmB;KACpB;IACjB,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,QAAQ,EAAE,WAAW;QACrB,cAAc,EAAE,mBAAmB;KACpB;CAClB,CAAC"}
1
+ {"version":3,"file":"stages.js","sourceRoot":"","sources":["../src/stages.ts"],"names":[],"mappings":"AAUA,gFAAgF;AAChF,8EAA8E;AAC9E,iFAAiF;AACjF,kFAAkF;AAClF,gFAAgF;AAChF,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,yEAAyE;IACzE,4EAA4E;IAC5E,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,SAAS;QACnB,cAAc,EAAE,mBAAmB;QACnC,IAAI,EAAE,QAAQ;KACC;IACjB,WAAW,EAAE;QACX,IAAI,EAAE,aAAa;QACnB,QAAQ,EAAE,QAAQ;QAClB,cAAc,EAAE,mBAAmB;QACnC,IAAI,EAAE,KAAK;KACI;IACjB,gBAAgB,EAAE;QAChB,IAAI,EAAE,mBAAmB;QACzB,QAAQ,EAAE,UAAU;QACpB,cAAc,EAAE,mBAAmB;QACnC,IAAI,EAAE,KAAK;KACI;IACjB,qBAAqB,EAAE;QACrB,IAAI,EAAE,yBAAyB;QAC/B,QAAQ,EAAE,gBAAgB;QAC1B,cAAc,EAAE,mBAAmB;QACnC,IAAI,EAAE,KAAK;KACI;IACjB,iBAAiB,EAAE;QACjB,IAAI,EAAE,oBAAoB;QAC1B,QAAQ,EAAE,cAAc;QACxB,cAAc,EAAE,mBAAmB;QACnC,IAAI,EAAE,KAAK;KACI;IACjB,sBAAsB,EAAE;QACtB,IAAI,EAAE,0BAA0B;QAChC,QAAQ,EAAE,oBAAoB;QAC9B,cAAc,EAAE,mBAAmB;QACnC,IAAI,EAAE,KAAK;KACI;IACjB,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,QAAQ,EAAE,WAAW;QACrB,cAAc,EAAE,mBAAmB;QACnC,IAAI,EAAE,QAAQ;KACC;IACjB,sBAAsB,EAAE;QACtB,IAAI,EAAE,0BAA0B;QAChC,QAAQ,EAAE,iBAAiB;QAC3B,cAAc,EAAE,mBAAmB;QACnC,IAAI,EAAE,QAAQ;KACC;IACjB,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,QAAQ,EAAE,WAAW;QACrB,cAAc,EAAE,mBAAmB;QACnC,IAAI,EAAE,QAAQ;KACC;IACjB,6EAA6E;IAC7E,oEAAoE;IACpE,cAAc,EAAE;QACd,IAAI,EAAE,iBAAiB;QACvB,QAAQ,EAAE,YAAY;QACtB,cAAc,EAAE,mBAAmB;QACnC,IAAI,EAAE,KAAK;KACI;IACjB,8EAA8E;IAC9E,8EAA8E;IAC9E,wEAAwE;IACxE,YAAY,EAAE;QACZ,IAAI,EAAE,eAAe;QACrB,QAAQ,EAAE,kBAAkB;QAC5B,cAAc,EAAE,mBAAmB;KACpB;IACjB,aAAa,EAAE;QACb,IAAI,EAAE,gBAAgB;QACtB,QAAQ,EAAE,mBAAmB;QAC7B,cAAc,EAAE,mBAAmB;KACpB;CAClB,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Threads (Meta) publishing client for the P12 public journal (issue #67).
3
+ * Mirrors linear-api.ts: injectable fetch + credentials from env or
4
+ * ~/.config/otto/threads.json. The Threads Graph API publishes in two steps —
5
+ * create a TEXT container, then publish it.
6
+ */
7
+ export type ThreadsAuth = {
8
+ token: string;
9
+ userId: string;
10
+ source: string;
11
+ };
12
+ export type ThreadsAuthDeps = {
13
+ env: NodeJS.ProcessEnv;
14
+ readFile: (path: string) => string | null;
15
+ home: string;
16
+ };
17
+ /** Canonical location of the stored Threads credentials (outside any repo). */
18
+ export declare function threadsConfigPath(home: string): string;
19
+ /**
20
+ * Resolve a Threads token + user id with precedence `OTTO_THREADS_TOKEN` +
21
+ * `OTTO_THREADS_USER_ID` → `~/.config/otto/threads.json` (`{ token, userId }`).
22
+ * Returns null when no source yields both a token and a user id.
23
+ */
24
+ export declare function resolveThreadsAuth(deps?: ThreadsAuthDeps): ThreadsAuth | null;
25
+ export type ThreadsErrorKind = "auth" | "network" | "api";
26
+ export declare class ThreadsApiError extends Error {
27
+ kind: ThreadsErrorKind;
28
+ constructor(message: string, kind: ThreadsErrorKind);
29
+ }
30
+ export type ThreadsClient = {
31
+ publish(text: string): Promise<{
32
+ id: string;
33
+ }>;
34
+ };
35
+ /**
36
+ * Create a Threads client. The two-step publish: create a TEXT container
37
+ * (`/{userId}/threads`), then publish it (`/{userId}/threads_publish`). `fetch`
38
+ * is injectable for tests; errors are classified as auth/network/api.
39
+ */
40
+ export declare function createThreadsClient(opts: {
41
+ token: string;
42
+ userId: string;
43
+ fetch?: typeof fetch;
44
+ baseUrl?: string;
45
+ }): ThreadsClient;
46
+ //# sourceMappingURL=threads-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"threads-api.d.ts","sourceRoot":"","sources":["../src/threads-api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,MAAM,WAAW,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5E,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC1C,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAcF,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,GAAE,eAAiC,GACtC,WAAW,GAAG,IAAI,CAuBpB;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,SAAS,GAAG,KAAK,CAAC;AAE1D,qBAAa,eAAgB,SAAQ,KAAK;IACxC,IAAI,EAAE,gBAAgB,CAAC;gBACX,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB;CAKpD;AAED,MAAM,MAAM,aAAa,GAAG;IAAE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC;AAI/E;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,aAAa,CAoChB"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Threads (Meta) publishing client for the P12 public journal (issue #67).
3
+ * Mirrors linear-api.ts: injectable fetch + credentials from env or
4
+ * ~/.config/otto/threads.json. The Threads Graph API publishes in two steps —
5
+ * create a TEXT container, then publish it.
6
+ */
7
+ import { readFileSync } from "node:fs";
8
+ import { homedir } from "node:os";
9
+ import { join } from "node:path";
10
+ const defaultAuthDeps = {
11
+ env: process.env,
12
+ readFile: (p) => {
13
+ try {
14
+ return readFileSync(p, "utf8");
15
+ }
16
+ catch {
17
+ return null;
18
+ }
19
+ },
20
+ home: homedir(),
21
+ };
22
+ /** Canonical location of the stored Threads credentials (outside any repo). */
23
+ export function threadsConfigPath(home) {
24
+ return join(home, ".config", "otto", "threads.json");
25
+ }
26
+ /**
27
+ * Resolve a Threads token + user id with precedence `OTTO_THREADS_TOKEN` +
28
+ * `OTTO_THREADS_USER_ID` → `~/.config/otto/threads.json` (`{ token, userId }`).
29
+ * Returns null when no source yields both a token and a user id.
30
+ */
31
+ export function resolveThreadsAuth(deps = defaultAuthDeps) {
32
+ const token = deps.env.OTTO_THREADS_TOKEN?.trim();
33
+ const userId = deps.env.OTTO_THREADS_USER_ID?.trim();
34
+ if (token && userId)
35
+ return { token, userId, source: "env" };
36
+ const path = threadsConfigPath(deps.home);
37
+ const raw = deps.readFile(path);
38
+ if (raw != null) {
39
+ try {
40
+ const o = JSON.parse(raw);
41
+ if (typeof o.token === "string" &&
42
+ o.token.trim() &&
43
+ typeof o.userId === "string" &&
44
+ o.userId.trim()) {
45
+ return { token: o.token.trim(), userId: o.userId.trim(), source: path };
46
+ }
47
+ }
48
+ catch {
49
+ // malformed → no credential from this source
50
+ }
51
+ }
52
+ return null;
53
+ }
54
+ export class ThreadsApiError extends Error {
55
+ kind;
56
+ constructor(message, kind) {
57
+ super(message);
58
+ this.name = "ThreadsApiError";
59
+ this.kind = kind;
60
+ }
61
+ }
62
+ const DEFAULT_BASE = "https://graph.threads.net/v1.0";
63
+ /**
64
+ * Create a Threads client. The two-step publish: create a TEXT container
65
+ * (`/{userId}/threads`), then publish it (`/{userId}/threads_publish`). `fetch`
66
+ * is injectable for tests; errors are classified as auth/network/api.
67
+ */
68
+ export function createThreadsClient(opts) {
69
+ const fetchImpl = opts.fetch ?? fetch;
70
+ const base = opts.baseUrl ?? DEFAULT_BASE;
71
+ if (!opts.token || !opts.userId) {
72
+ throw new ThreadsApiError("missing Threads credentials", "auth");
73
+ }
74
+ const post = async (url) => {
75
+ let res;
76
+ try {
77
+ res = await fetchImpl(url, { method: "POST" });
78
+ }
79
+ catch (e) {
80
+ throw new ThreadsApiError(`Threads request failed: ${e.message}`, "network");
81
+ }
82
+ if (!res.ok) {
83
+ throw new ThreadsApiError(`Threads API returned ${res.status}`, "api");
84
+ }
85
+ const json = (await res.json());
86
+ if (typeof json.id !== "string") {
87
+ throw new ThreadsApiError("Threads API response missing id", "api");
88
+ }
89
+ return { id: json.id };
90
+ };
91
+ return {
92
+ async publish(text) {
93
+ const tok = encodeURIComponent(opts.token);
94
+ const create = `${base}/${opts.userId}/threads?media_type=TEXT&text=${encodeURIComponent(text)}&access_token=${tok}`;
95
+ const { id: creationId } = await post(create);
96
+ const publish = `${base}/${opts.userId}/threads_publish?creation_id=${encodeURIComponent(creationId)}&access_token=${tok}`;
97
+ return post(publish);
98
+ },
99
+ };
100
+ }
101
+ //# sourceMappingURL=threads-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"threads-api.js","sourceRoot":"","sources":["../src/threads-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Git-worktree isolation for sub-agent fan-out (issue #66 P11). Each parallel
3
+ * sub-agent runs in its own worktree under `.otto-tmp/wt/<id>` — a separate
4
+ * working directory sharing the repo's object store, so the agents never clobber
5
+ * each other's files. The dir is inside the workspace, so the native OS sandbox
6
+ * still confines writes. `.otto-tmp/` is already gitignored.
7
+ */
8
+ /**
9
+ * Create an isolated worktree at HEAD under `.otto-tmp/wt/<id>`. `--detach` so
10
+ * the sub-agent commits onto a detached HEAD without creating or moving a branch.
11
+ * `cleanup()` removes the worktree (and its dir); it is idempotent and safe to
12
+ * call from a `finally`.
13
+ */
14
+ export declare function createWorktree(workspaceDir: string, id: string): {
15
+ dir: string;
16
+ cleanup: () => void;
17
+ };
18
+ /**
19
+ * Prune leftover worktrees from a crashed prior run: remove the `.otto-tmp/wt`
20
+ * tree wholesale, then GC git's worktree registry so the stale entries don't
21
+ * linger. Call once before a fan-out round.
22
+ */
23
+ export declare function reapWorktrees(workspaceDir: string): void;
24
+ //# sourceMappingURL=worktree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree.d.ts","sourceRoot":"","sources":["../src/worktree.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,YAAY,EAAE,MAAM,EACpB,EAAE,EAAE,MAAM,GACT;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CAetC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAGxD"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Git-worktree isolation for sub-agent fan-out (issue #66 P11). Each parallel
3
+ * sub-agent runs in its own worktree under `.otto-tmp/wt/<id>` — a separate
4
+ * working directory sharing the repo's object store, so the agents never clobber
5
+ * each other's files. The dir is inside the workspace, so the native OS sandbox
6
+ * still confines writes. `.otto-tmp/` is already gitignored.
7
+ */
8
+ import { rmSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ import { git } from "./git.js";
11
+ /**
12
+ * Create an isolated worktree at HEAD under `.otto-tmp/wt/<id>`. `--detach` so
13
+ * the sub-agent commits onto a detached HEAD without creating or moving a branch.
14
+ * `cleanup()` removes the worktree (and its dir); it is idempotent and safe to
15
+ * call from a `finally`.
16
+ */
17
+ export function createWorktree(workspaceDir, id) {
18
+ const rel = join(".otto-tmp", "wt", id);
19
+ const dir = join(workspaceDir, rel);
20
+ // Remove any stale worktree at this path first (a crashed prior run), then add.
21
+ git(["worktree", "remove", "--force", dir], workspaceDir);
22
+ rmSync(dir, { recursive: true, force: true });
23
+ git(["worktree", "add", "--detach", dir, "HEAD"], workspaceDir);
24
+ let cleaned = false;
25
+ const cleanup = () => {
26
+ if (cleaned)
27
+ return;
28
+ cleaned = true;
29
+ git(["worktree", "remove", "--force", dir], workspaceDir);
30
+ rmSync(dir, { recursive: true, force: true });
31
+ };
32
+ return { dir, cleanup };
33
+ }
34
+ /**
35
+ * Prune leftover worktrees from a crashed prior run: remove the `.otto-tmp/wt`
36
+ * tree wholesale, then GC git's worktree registry so the stale entries don't
37
+ * linger. Call once before a fan-out round.
38
+ */
39
+ export function reapWorktrees(workspaceDir) {
40
+ rmSync(join(workspaceDir, ".otto-tmp", "wt"), { recursive: true, force: true });
41
+ git(["worktree", "prune"], workspaceDir);
42
+ }
43
+ //# sourceMappingURL=worktree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree.js","sourceRoot":"","sources":["../src/worktree.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,YAAoB,EACpB,EAAU;IAEV,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;IACpC,gFAAgF;IAChF,GAAG,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,YAAY,CAAC,CAAC;IAC1D,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,GAAG,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,YAAY,CAAC,CAAC;IAChE,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,MAAM,OAAO,GAAG,GAAS,EAAE;QACzB,IAAI,OAAO;YAAE,OAAO;QACpB,OAAO,GAAG,IAAI,CAAC;QACf,GAAG,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,YAAY,CAAC,CAAC;QAC1D,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC,CAAC;IACF,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;AAC1B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,YAAoB;IAChD,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAChF,GAAG,CAAC,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,YAAY,CAAC,CAAC;AAC3C,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phamvuhoang/otto-core",
3
- "version": "0.18.0",
3
+ "version": "0.20.0",
4
4
  "description": "Claude Code AFK orchestration: iteration loop, native-sandbox runner, template renderer.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -0,0 +1,19 @@
1
+ You are a strict pre-publication secrecy reviewer. A note is about to be posted
2
+ publicly by an autonomous coding agent. Your ONLY job is to prevent leaks.
3
+
4
+ Decide whether the note below is SAFE to post. Treat it as UNSAFE if a reader
5
+ could identify the specific repository, product, company, person, or customer —
6
+ or if it contains code, secrets, paths, URLs, names, or any private/internal
7
+ detail. If there is ANY doubt whatsoever, answer UNSAFE.
8
+
9
+ Respond with EXACTLY one line and nothing else:
10
+
11
+ <journal-verdict>SAFE</journal-verdict>
12
+
13
+ or
14
+
15
+ <journal-verdict>UNSAFE</journal-verdict>
16
+
17
+ <note>
18
+ {{ INPUTS }}
19
+ </note>
@@ -0,0 +1,16 @@
1
+ You are _a coding agent's field notes_ — you share short, generic lessons about
2
+ the craft of software work, never about any specific project.
3
+
4
+ Below is one durable learning. Rewrite it as a SINGLE first-person field note
5
+ (2–4 sentences, under 400 characters) capturing the GENERAL craft lesson.
6
+
7
+ STRICT RULES:
8
+
9
+ - No project, product, company, person, file, path, tool, version, or ticket names.
10
+ - No code, commands, URLs, or identifiers of any kind.
11
+ - If the learning cannot be generalized without specifics, output exactly: SKIP
12
+ - Output ONLY the note text (or SKIP). No preamble, no quotes, no markdown.
13
+
14
+ <learning>
15
+ {{ INPUTS }}
16
+ </learning>
package/templates/plan.md CHANGED
@@ -87,9 +87,31 @@ Use the `Write` tool. An ordered checklist of **bite-sized, testable tasks**, on
87
87
  Keep tasks ordered so each is gated on the prior; name the test file that pins
88
88
  each task.
89
89
 
90
+ ## 4b. WRITE THE TASK GRAPH (optional) — `.otto/tasks/<task-key>/tasks.json`
91
+
92
+ Use the `Write` tool to also emit a machine-readable task graph so Otto can fan
93
+ independent tasks out to isolated sub-agents (issue #66 P11). Shape:
94
+
95
+ ```json
96
+ {
97
+ "version": 1,
98
+ "tasks": [
99
+ { "id": "t1", "title": "<short>", "fileScope": ["path/you/expect/to/touch"], "dependsOn": [], "parallelSafe": true },
100
+ { "id": "t2", "title": "<short>", "fileScope": ["other/path"], "dependsOn": ["t1"], "parallelSafe": false }
101
+ ]
102
+ }
103
+ ```
104
+
105
+ Rules: `id` unique; `dependsOn` lists earlier task ids that must land first;
106
+ `fileScope` lists the files the task is expected to edit; set `parallelSafe`
107
+ **true only when** the task shares no files and no ordering with its siblings —
108
+ **be conservative and default to `false` when unsure**. This file is OPTIONAL:
109
+ if the work does not decompose into cleanly independent tasks, omit it and the
110
+ run proceeds sequentially. A malformed file is ignored (fan-out simply disabled).
111
+
90
112
  ## 5. COMMIT
91
113
 
92
- Commit ONLY the spec + plan (and this is the whole runno code). Use a
93
- `docs(plan):` or `chore(plan):` commit. Then print a one-line summary of the
94
- task-key and the number of plan tasks authored. Do not implement; the human
95
- reviews the plan next.
114
+ Commit ONLY the spec + plan + (if written) the task graphthis is the whole
115
+ run, no code. Use a `docs(plan):` or `chore(plan):` commit. Then print a one-line
116
+ summary of the task-key and the number of plan tasks authored. Do not implement;
117
+ the human reviews the plan next.
@@ -0,0 +1,32 @@
1
+ {{ RESUME }}
2
+
3
+ <learnings>
4
+
5
+ !?`cat ./.otto/LEARNINGS.md|||_No learnings recorded yet._`
6
+
7
+ </learnings>
8
+
9
+ <task>
10
+
11
+ You are implementing **one** task in an isolated worktree, as part of a parallel
12
+ fan-out (issue #66 P11). Do the whole task and nothing else.
13
+
14
+ **Task:** {{ TASK_TITLE }}
15
+
16
+ **Files you are expected to touch (your scope — do not edit files outside it):**
17
+
18
+ ```
19
+ {{ TASK_SCOPE }}
20
+ ```
21
+
22
+ Stay strictly within that file scope: a sibling sub-agent owns every other file
23
+ in parallel, so editing outside your scope will collide at merge time. If the
24
+ task genuinely cannot be done without touching a file outside the scope, do as
25
+ much as you safely can within scope and note the gap in your commit message.
26
+
27
+ Write the failing test first where it applies, implement, run the relevant
28
+ feedback loop, and make a single focused commit for this task. Do not push.
29
+
30
+ </task>
31
+
32
+ @include:prompt.md